<?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: kpoluektov</title>
    <description>The latest articles on DEV Community by kpoluektov (@kpoluektov).</description>
    <link>https://dev.to/kpoluektov</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%2F1032588%2Fbb45793c-c5b9-4a5e-bea2-06de70ce3130.png</url>
      <title>DEV Community: kpoluektov</title>
      <link>https://dev.to/kpoluektov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kpoluektov"/>
    <language>en</language>
    <item>
      <title>Oracle to PostgreSQL code migration pain</title>
      <dc:creator>kpoluektov</dc:creator>
      <pubDate>Thu, 17 Aug 2023 03:51:52 +0000</pubDate>
      <link>https://dev.to/kpoluektov/oracle-to-postgresql-code-migration-pain-41pm</link>
      <guid>https://dev.to/kpoluektov/oracle-to-postgresql-code-migration-pain-41pm</guid>
      <description>&lt;h2&gt;
  
  
  Pessimistic exception
&lt;/h2&gt;

&lt;p&gt;Well, who will use pessimistic locks now in times of eventual consistency? But, actually the approach still is the main pattern to handling double-entry bookkeeping. In rdbms world it is quite easy to provide the pattern with the &lt;code&gt;select ... for update [no]wait&lt;/code&gt; statement. &lt;br&gt;
"So what's the catch?” you can ask? After all, the ANSI SQL complaint construction is supported in both rdbms (well, in the &lt;code&gt;nowait&lt;/code&gt;part, for sure)! Exactly! &lt;code&gt;for update nowait&lt;/code&gt; works fine. But there is a big difference with exception handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;object TestLockException extends App {
  val updateSql = "select 1 from type_test_table where id = ? for update nowait";
  def execLockTest(exec: DBExecute, id: Integer) {
    val check: Try[Unit] = Using(exec.getConnection) { conn =&amp;gt;
      def LockAndLog(conn: Connection): Unit = {
        var isLockSuccess = true
        val updStatement = conn.prepareCall(updateSql)
        try {
          updStatement.setInt(1, id /*potentially locked row ID*/)
          updStatement.execute()
        } catch {
          case e: SQLException if exec.isRowLockException(e) =&amp;gt; println("Row is already locked"); isLockSuccess = false
        } finally {
          exec.logIt(conn, id, isLockSuccess)
        }
      }
      conn.setAutoCommit(false)
      // place for some prep-code including savepoint
      LockAndLog(conn)
      // place for some after-code including overall transaction control - commit or rollback
    }
    println(exec.getClass.getName + " test is " + check)
  }
  execLockTest(new OraExecute(), 1)
  execLockTest(new PGExecute, 1)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key function is LockAndLog: we firstly try to lock the row with the lock_not_available/resource_busy exception catching, then we write result in the log_table.&lt;/p&gt;

&lt;p&gt;Let's run the code snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sbt:NotesExamples&amp;gt; run
[info] running pol.notes.lock.TestLockException TestLockException
pol.notes.lock.OraExecute test is Success(())
pol.notes.lock.PGExecute test is Success(())
[success] Total time: 2 s, completed Aug 16, 2023, 8:47:09 PM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far so good. And now let's check: what happens if the row lock is already set. &lt;br&gt;
Blocking a row in PostgreSQL&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgres=# \set AUTOCOMMIT off
postgres=# select id from pol.type_test_table where id = 1 for update;
 id
----
  1
(1 row)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and in Oracle  at once&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SQL&amp;gt; select id from type_test_table where id = 1 for update nowait;

        ID
----------
         1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now rerun our code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sbt:NotesExamples&amp;gt; run
[info] running pol.notes.lock.TestLockException TestLockException
Row is already locked
pol.notes.lock.OraExecute test is Success(())
Row is already locked
pol.notes.lock.PGExecute test is Failure(org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block)
[success] Total time: 3 s, completed Aug 16, 2023, 8:09:12 PM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel the difference.&lt;/p&gt;

&lt;p&gt;We will not delve into the study of the reasons for this difference. We do migration and we need a solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fastest one - jdbc driver settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;autosave (String) Default never&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Let's add the property&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;override def getConnection(): Connection = {
  Class.forName("org.postgresql.Driver")
  val url = "jdbc:postgresql://localhost:5432/postgres?user=*****&amp;amp;password=*****&amp;amp;currentSchema=pol"
  props.setProperty("autosave", "always")
  DriverManager.getConnection(url, props)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and check&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sbt:NotesExamples&amp;gt; run
[info] running pol.notes.lock.TestLockException TestLockException
pol.notes.lock.OraExecute test is Success(())
pol.notes.lock.PGExecute test is Success(())
[success] Total time: 1 s, completed Jul 24, 2023 10:18:06 PM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila&lt;br&gt;
Everything seems to be the same now? Looks like yes. But if you have a really high-load system, pay attention to the remark:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
Note&lt;br&gt;
autosave might result in severe performance issues for long transactions, as PostgreSQL® backend is not optimized for the case of long transactions and lots of savepoints.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jdbc.postgresql.org/documentation/server-prepare/"&gt;https://jdbc.postgresql.org/documentation/server-prepare/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setting will lead to work in the "sub transactions" mode for each SQL statement. In addition to cited limit the number of savepoints, using this setting can lead to a significantly higher load on the internal transaction counter and, as a result, reduce the wraparound interval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rewrite the code&lt;/strong&gt; - add an explicit rollback (or rollback to savepoint) to the exception handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    def LockAndLog(conn: Connection): Unit = {
      var isLockSuccess = true
      val updStatement = conn.prepareCall(updateSql)
      try {
        updStatement.setInt(1, id /*potentially locked row ID*/)
        updStatement.execute()
      } catch {
        case e: SQLException if exec.isRowLockException(e) =&amp;gt; conn.rollback(); println("Row is already locked"); isLockSuccess = false
      } finally {
        exec.logIt(conn, id, isLockSuccess)
      }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code snippets are available at &lt;a href="https://github.com/kpoluektov/pol.notes"&gt;https://github.com/kpoluektov/pol.notes&lt;/a&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
    </item>
  </channel>
</rss>
