<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Amrut Prabhu</title>
    <description>The latest articles on DEV Community by Amrut Prabhu (@amrutprabhu).</description>
    <link>https://dev.to/amrutprabhu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F21676%2F091d907c-bf63-4117-9bbe-95ed698258ac.jpg</url>
      <title>DEV Community: Amrut Prabhu</title>
      <link>https://dev.to/amrutprabhu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amrutprabhu"/>
    <language>en</language>
    <item>
      <title>Spring Boot: JPA Bulk Insert Performance by 100 times.</title>
      <dc:creator>Amrut Prabhu</dc:creator>
      <pubDate>Mon, 25 Jan 2021 22:46:12 +0000</pubDate>
      <link>https://dev.to/amrutprabhu/spring-boot-jpa-bulk-insert-performance-by-100-times-fn4</link>
      <guid>https://dev.to/amrutprabhu/spring-boot-jpa-bulk-insert-performance-by-100-times-fn4</guid>
      <description>&lt;p&gt;So I was facing a problem where I wanted to insert millions of records into the database which needed to be imported from the file.&lt;/p&gt;

&lt;p&gt;I did some research around this and I would like to share with you what I found which helped me improve the insert records throughput by nearly 100 times.&lt;/p&gt;

&lt;p&gt;Initially when I was just trying to do bulk insert using spring JPA’s saveAll method, I was getting a performance of about 185 seconds per 10,000 records . After doing the following changes below, the performance to insert 10,000 records was just in &lt;strong&gt;4.3&lt;/strong&gt; seconds .&lt;/p&gt;

&lt;p&gt;Yes, &lt;strong&gt;4.3&lt;/strong&gt; Seconds for &lt;strong&gt;10k&lt;/strong&gt; records.&lt;/p&gt;

&lt;p&gt;So to achieve this, I had to change the way I was inserting data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Change the number of records while inserting.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When I was inserting initially, I was pushing all the 10k records from the list directly by calling the saveAll method. I changed this to the batch size of 30. You could also increase the batch size to even 60, But it doesn’t half the time taken to insert records. (See the table below)&lt;/p&gt;

&lt;p&gt;For this you need to set the hibernate property &lt;code&gt;batch_size=30&lt;/code&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring.jpa.properties.hibernate.jdbc.batch_size=30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I added the following connection string properties :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cachePrepStmts=true
useServerPrepStmts=true
rewriteBatchedStatements=true

e.g
jdbc:mysql://localhost:3306/BOOKS_DB?serverTimezone=UTC&amp;amp;cachePrepStmts=true&amp;amp;useServerPrepStmts=true&amp;amp;rewriteBatchedStatements=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Send Batched records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Changed the code for inserting, so that saveAll methods get batch sizes of 30 to insert as per what we also set in the properties file. A very crude implementation of something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (int i = 0; i &amp;lt; totalObjects; i = i + batchSize) {
    if( i+ batchSize &amp;gt; totalObjects){
        List&amp;lt;Book&amp;gt; books1 = books.subList(i, totalObjects - 1);
        repository.saveAll(books1);
        break;
    }
    List&amp;lt;Book&amp;gt; books1 = books.subList(i, i + batchSize);
    repository.saveAll(books1);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduced the time by a little, dropped from 185 secs to 153 Secs. Thats approximately 18% improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Change the ID generation strategy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This made a major impact.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Initially, I was using the &lt;code&gt;@GeneratedValue&lt;/code&gt; annotation with strategy i.e &lt;code&gt;GenerationType.IDENTITY&lt;/code&gt; on my entity class.&lt;/p&gt;

&lt;p&gt;Hibernate has disabled batch update with this strategy, Because it has to make a select call to get the id from the database to insert each row. You can read more about it &lt;a href="https://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html/ch15.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I changed the strategy to &lt;code&gt;SEQUENCE&lt;/code&gt; and provided a sequence generator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Book {
    @Id
    @GeneratedValue(strategy = SEQUENCE, generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "seq", initialValue = 1)
    private Long id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This drastically changed the insert performance as Hibernate was able to leverage bulk insert.&lt;/p&gt;

&lt;p&gt;From the previous performance improvement of 153 secs, the time to insert 10k records reduced to only &lt;strong&gt;9&lt;/strong&gt; secs. Thats an increase in performance by nearly &lt;strong&gt;95%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: MySQL doesn’t support creating sequences.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To get around this, I created a table with the name of the sequence having a single field called &lt;code&gt;next_val&lt;/code&gt;. Then I added a single row with an initial value.&lt;/p&gt;

&lt;p&gt;For the above sequence I created the following :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE `seq` (
  `next_val` bigint(20) DEFAULT NULL
);
INSERT INTO `seq` (`next_val`) VALUES(1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hibernate then used this table as a sequence generator.&lt;/p&gt;

&lt;p&gt;Next, I pushed it further to use higher batch sizes and I noticed that doubling the batch size does not double down on time. The time to insert only gradually reduces. You can see this bellow.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvzpjvcenr3t9ktavmq7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvzpjvcenr3t9ktavmq7.png" alt="statitics" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most optimal batch size for my case was a &lt;strong&gt;1000&lt;/strong&gt; which took around &lt;strong&gt;4.39&lt;/strong&gt; secs for 10K records. After that, I saw the performance degrading as you can see in the graph.&lt;/p&gt;

&lt;p&gt;Here are the stats I got.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqx0jtyanbaybnv1jqv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqx0jtyanbaybnv1jqv7.png" alt="batch stats" width="222" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As always you can find the code on my &lt;a href="https://github.com/amrutprabhu/spring-boot-jpa-bulk-insert-performance" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>hibernate</category>
      <category>jpa</category>
      <category>bulkinsert</category>
    </item>
    <item>
      <title>Spring Boot : Handle AWS RDS password change or rotation without restarting</title>
      <dc:creator>Amrut Prabhu</dc:creator>
      <pubDate>Thu, 05 Nov 2020 20:48:16 +0000</pubDate>
      <link>https://dev.to/amrutprabhu/spring-boot-handle-aws-rds-password-change-or-rotation-without-restarting-301l</link>
      <guid>https://dev.to/amrutprabhu/spring-boot-handle-aws-rds-password-change-or-rotation-without-restarting-301l</guid>
      <description>&lt;p&gt;This article is about how you can handle AWS RDS secrets rotation without restarting your Spring Boot application.&lt;/p&gt;

&lt;p&gt;I had this problem wherein I had to update my database connection whenever the database password was updated for my AWS RDS instance. This can be because of a monthly password rotation policy or may be the database credentials got compromised and you want all your running applications to keep running even when the database password are changed.&lt;/p&gt;

&lt;p&gt;To solve this kind of a problem, AWS provides a library that will handle this updating of the database connection without even restarting your Spring Boot application.&lt;/p&gt;

&lt;p&gt;AWS has an open source library called &lt;a href="https://github.com/aws/aws-secretsmanager-jdbc"&gt;AWS Secrets Manager JDBC&lt;/a&gt; , that handles database connection while your application is running and talking to the RDS instance.&lt;/p&gt;

&lt;p&gt;Let's see how this works.&lt;/p&gt;

&lt;p&gt;Firstly, Add the following dependency in the build file. Considering maven it would be as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.amazonaws.secretsmanager&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;aws-secretsmanager-jdbc&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0.5&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, specify the JDBC datasource URL with the scheme &lt;code&gt;jdbc-secretsmanager&lt;/code&gt; instead of &lt;code&gt;jdbc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc-secretsmanager:mysql://database-host:3306/rotate_db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, You need to specify the driver class name. For this article we will stick to MySQL RDS instance. So it’s going to be &lt;code&gt;com.amazonaws.secretsmanager.sql.AWSSecretsManagerMySQLDrive&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This library also requires the database specific connection library. So you will need to add the MySQL connector library, Which is commonly the artifact &lt;code&gt;mysql-connector-java&lt;/code&gt;. This will be used to make the actual connection with the database.&lt;/p&gt;

&lt;p&gt;In case you are dealing with other databases you can find the corresponding drivers from the source code &lt;a href="https://github.com/aws/aws-secretsmanager-jdbc/tree/master/src/main/java/com/amazonaws/secretsmanager/sql"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, Create an AWS secret for the RDS instance using the database credentials section in the AWS Secrets Manager.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aTux-uN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/662/1%2AUS2E33T4UPV4x2HE-BkBqA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aTux-uN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/662/1%2AUS2E33T4UPV4x2HE-BkBqA.png" alt="Secrets manager"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--il2lYu9s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1796/1%2A_8lAxbdanMqA5aP2w966yA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--il2lYu9s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1796/1%2A_8lAxbdanMqA5aP2w966yA.png" alt="Secrets Manager Configure automatic rotation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, In the properties file application.yaml, specify the secret name you just created as the username and you don’t have to specify any password as it’s now stored in the secrets manager.&lt;/p&gt;

&lt;p&gt;Your property file should look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc-secretsmanager:mysql://database-host:3306/rotate_db
    username: secret/rotation
    driver-class-name: com.amazonaws.secretsmanager.sql.AWSSecretsManagerMySQLDriver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, For the application to communicate with AWS and fetch the secret value, you would have to have AWS CLI setup and configured. &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html"&gt;Here&lt;/a&gt; is the link to it.&lt;/p&gt;

&lt;p&gt;Once you have this in place, your application can connect to AWS by exporting the environment variable &lt;code&gt;AWS_PROFILE&lt;/code&gt; with the profile you setup while configuring the AWS configuration.&lt;br&gt;
With this, you are done with the changes.&lt;/p&gt;

&lt;p&gt;Now start the application and it should be able to communicate with AWS Secrets Manager to fetch the credentials and start communicating with the AWS RDS instance.&lt;/p&gt;

&lt;p&gt;You can test this by clicking on the rotate secret option in the secret which will generate a new password for database and check the communication with the database.&lt;/p&gt;

&lt;p&gt;Here is a &lt;a href="https://github.com/amrutprabhu/spring-boot-aws-rds-password-rotation"&gt;GitHub link&lt;/a&gt; to my implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bonus:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This also works if you have liquibase integration in place . You just have to specify the same URL in the liquibase configuration and the database secret as the username and the liquibase setup will work for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc-secretsmanager:mysql://database-host:3306/rotate_db
    username: secret/rotation
    driver-class-name: com.amazonaws.secretsmanager.sql.AWSSecretsManagerMySQLDriver

  liquibase:
    url: jdbc-secretsmanager:mysql://database-host:3306/rotate_db
    user: secret/rotation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enjoy!! Have Fun!!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>java</category>
      <category>springboot</category>
      <category>password</category>
    </item>
  </channel>
</rss>
