SQLite ... 1 app to write, multiple apps to read same SQLite file/tables

SQLite … seemingly easy?

Objective

iWriter writes to a few SQLite tables, and multiple iReader instances should concurrently READONLY those same SQLite tables

iWrite.exe - reads TPS files and writes to SQLite
iRead.exe - READONLY on SQLite tables written by iWriter

A. DCT

Same for both apps one table
zEventLog - SQLite Driver

Reading thru the SQLite threads here, I added /WAL to the zEventLog tables Driver Option which throws “WAL is not a valid switch”

B. iReader

Global Properties / File Control

  • Unchecked Enclose RI
    File Open Mode - Other
    User Access - Read Only
    Other Access - Any Access

C. iWriter

Global Properties / File Control

  • Checked Enclose RI
    File Open Mode - Other
    User Access - Read/Write
    Other Access - Deny None

When I run iWriter and iReader concurrently, iWriter throws Access Denied (5) on zEventLog

What am I missing?

TIA

Are you using the SQLite or SQLite2 driver?
/WAL is supported by SQLite2, not by SQLite.

If using SQLite you can use a simple prop:sql, or even an external client, to set the database into WAL mode. Its a change to the SQLite file, so is persistent znd only needs to be done once. It applies to all programs reading that file.

It can only be used if ALL processes accessing the data file are on the same PC.

That said, the locks used by the SQLite driver are very aggressive- the SQLite2 driver is far more friendly to multiple threads and processes.

1 Like

send(‘PRAGMA journal_mode=WAL’) in driver options is how to set WAL in the original driver. Bruce’s “very aggressive” basically means that the original driver locks the file when it shouldn’t, so doing practically anything it locks itself out, let alone any other process. I’m hoping to check out the SQLite2 driver in the next few days.

1 Like

Just wondering, why SQLite and not a more robust database for multiple users?

Hi Bruce

I’m applying /WAL in the DCT driver option which is using the default Clarion SQLite driver.

However, when I deploy, I am using the v3.42 SQLite3.dll

TY Jon.

I’ll give that a try.

~P

The current solution is undergoing a complete rewrite to Postgres while the TPS version needs to be on life support :slight_smile:

Chose SQLite as the lightweight SQL option (we ported key features and normalized the db) for Nettalk Webserver primarily reports and analytics sorely cumbersome or lacking in the TPS version

Having the power of SQL was needed and employing views would be as simple as re-creating the views verbatim in Postgres - thus leveraging code and time…hence the single process to write and multiple processes to read only

2 Likes

Hi Bruce,

Ack. All processes are on the same computer.

I’ll embark on the SQLite2 path tomorrow.

I’m applying /WAL in the DCT driver option which is using the default Clarion SQLite driver.

That is not supported by the SQLite driver. Only SQLite2.

I have a similar use case for SQLite. Our TPS application can’t be reasonably ported to a client/server DBMS, but SQLite works as a great replacement for it.

There are, however, some dragons you should consider:

1. Network Access Limitations

If your app needs to run over a network (which is common for TPS-based apps), SQLite3’s WAL mode is not the solution. It will corrupt as soon as two stations try to access the database.

2. Clarion Driver Limitations

It’s not that SQLite has multi-user limitations per se (that’s critical to understand). The issue is with the Clarion SQLite3 driver implementation. In non-WAL mode, a read statement blocks any writes. Since an operation like SET(x); NEXT(x) creates an open-ended statement that is never consumed, you get locking errors in any multi-tenant setting.

Manual resets or run-to-completion both solve this issue, but since the low-level API isn’t exposed, you’re screwed in the Clarion ecosystem unless the SQLite2 driver by CapeSoft solves the issue.

Solution

I hand-rolled a SQLite3 wrapper and created a mapping layer between SQLite3 and Clarion record structures. The result is that I don’t get concurrency issues from SQLite3 while being free to slowly port read-heavy or complex workloads from TPS to SQLite3 proper.

Feel free to AMA.

While I think I agree with what you are saying, I think it could be expressed more clearly:

In non-WAL mode, a Clarion read (such as a SET/NEXT to fill a browse) locks the database. Since this is an open-ended statement that is never consumed (until the browse is closed), you get locking errors even in a single user setting – the form cannot get a lock on the database to write its changes back because the browse already (unnecessarily) locked the database.

1 Like

You should not be getting situations where driver locks the db and does not allow you to write back to it. I mean, a SET/NEXT loop should acquire a read lock to the db and self-escalating to an intent-to-write to exclusive should not cause issues if they are all done within the same app (thread?) and the reader and the writer share the owner string.

It’s just that sqlite3 engine (not driver) won’t allow any connection to mutate the database while it’s being read by another.

Thx Tom

The Capesoft DriverKit - SQL version has resolved the issue.

For the read-heavy tables, I load from TPS to SQLite on an interval (those that have changed) and serve the data via Nettalk webserver REST

I primarily needed the power of SQL for complex queries

As a side note, the DriverKit methods - FromQueue(table, queue) and ToQueue(table, queue) +Upsert are worth the cost alone

Cheers!

1 Like

It does. On the first NEXT the result set is read into the driver, which NEXT then iterates theough. It does this on a “page” basis (you can set the page size.)

So the “first NEXT” does what it needs to do, then releases the database for others to use.

1 Like