What are the expected semantics for a read-only database with multiple processes?

Mark Raynsford via Hsqldb-user <[email protected]> Sat, 29 Oct 2022 13:44:32 +0000
Newsgroups gmane.comp.java.hsqldb.user
Organization io7m.com
Message-ID <[email protected]>
Hello!

I have an application where (for reasons that aren't all that
interesting), I'm not able to have a server process mediating access to
a database. I noticed that the HSQLDB documentation makes various
references to it being compatible with multi-process use. In my case,
there'll be one writer process that is occasionally started and will
occasionally write some data to a database. There'll also be multiple
reader processes that attempt to read from the database somewhat more
frequently.

The following pair of test programs show behaviour that seems less than
ideal, however:

--8<--
public final class Writer
{
  private Writer()
  {

  }

  public static void main(
    final String[] args)
    throws Exception
  {
    try (var c =
           DriverManager.getConnection("jdbc:hsqldb:file:testdb", "SA","")) { 
      c.setAutoCommit(false);

      try (var st = c.prepareStatement("create cached table t (x integer not null)")) { 
        st.execute();
        c.commit();
      } catch (final SQLException e) {
        // OK
      }

      int x = 0;
      while (true) {
        try (var st = c.prepareStatement("insert into t (x) values
      (?)")) { st.setInt(1, x);
          st.execute();
          c.commit();
          System.out.println(x);
        }
        ++x;
        Thread.sleep(1_000L);
      }
    }
  }
}
--8<--


--8<--
public final class Reader
{
  private Reader()
  {

  }

  public static void main(
    final String[] args)
    throws Exception
  {
    try (var c =
           DriverManager.getConnection(
             "jdbc:hsqldb:file:testdb;readonly=true",
             "SA",
             "")) {
      c.setAutoCommit(false);

      while (true) {
        try (var st = c.prepareStatement(
          "select t.x from t order by t.x desc limit 1")) {
          try (var rs = st.executeQuery()) {
            rs.next();
            System.out.println(rs.getInt(1));
          }
        }
        Thread.sleep(1_000L);
      }
    }
  }
}
--8<--

The Writer program opens a database and starts adding rows with an
incrementing integer once per second. The output isn't surprising:

0
1
2
3
4
5
...

The Reader program opens a database in read-only mode, and
(inefficiently!) reads whatever is the highest integer in the
table right now, once per second. However, the output is surprising:

4
4
4
4
4
...

I suspect what it's doing is some kind of caching of the first result
of the query, and it's never invalidating the cache because it assumes
the database is read-only and therefore can't change. Is this correct?

Is there a way to get SQLite[0]-like behaviour where readers see
up-to-date information, and simply block if there are writers changing
the data at the time they try to read?

[0] https://www.sqlite.org

-- 
Mark Raynsford | https://www.io7m.com