Skip to content

Commit e9da7ef

Browse files
committed
Started markdown documentation in a docs folder
1 parent 2890044 commit e9da7ef

File tree

3 files changed

+64
-0
lines changed

3 files changed

+64
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
# SQLite ORM
1515
SQLite ORM light header only library for modern C++. Please read the license precisely. The project has AGPL license for open source project and MIT license after purchasing it for 50$ (using [PayPal](https://paypal.me/fnc12) or any different way (contact using email [email protected])).
1616

17+
Documentation is found in [docs](docs/home.md).
18+
1719
# Status
1820
| Branch | Travis | Appveyor |
1921
| :----- | :----- | :------- |

docs/home.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Topics
2+
3+
* [Thread Safety](thread-safety.md)

docs/thread-safety.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Thread safety and concurrent database access
2+
3+
There are two layers to consider regarding concurrent database access and thread safety: the SQLite layer and the `sqlite_orm` layer.
4+
5+
## 1. The SQLite layer
6+
7+
Everything depends on how SQLite was compiled.
8+
9+
`sqlite_orm` does not add any layer to SQLite's multi-threading policy or capability, nor does it impose additional restrictions.
10+
11+
At the time of writing, there are no runtime database connection options in `sqlite_orm` that change SQLite's concurrent access modes.
12+
13+
Other SQLite features affect concurrent database access, data visibility, and on-disk integrity; for example: journal mode (WAL), handling the `busy` state, transactions, and syncing to disk.
14+
15+
Schema synchronization or data operations that require coherent, atomic behavior must be performed inside a transaction and often require a busy handler. This is especially important when accessing a database from multiple processes.
16+
17+
Familiarize yourself with SQLite's threading and concurrency documentation for full details:
18+
- [Can multiple applications or multiple instances of the same application access a single database file at the same time?](https://www.sqlite.org/faq.html#q5) — yes; SQLite uses file locks to coordinate concurrent access to the database file itself. Restrictions apply.
19+
- [Is SQLite threadsafe?](https://www.sqlite.org/faq.html#q6) — yes; SQLite uses locks for a database connection.
20+
- [Using SQLite In Multi-Threaded Applications](https://www.sqlite.org/threadsafe.html)
21+
22+
## 2. The `sqlite_orm` layer
23+
24+
A program commonly uses a single `storage` instance created by `sqlite_orm::make_storage()` to represent the database. As of `sqlite_orm` v1.10, a `storage` instance can be safely shared; see the considerations below.
25+
26+
Unless you are using an in-memory database, no connection is established by default when you define the `storage` object. On each database access, a connection is opened and closed automatically. It is, of course, possible to open the database permanently, as described in the "A word about performance" section below.
27+
28+
Background: each `storage` instance maintains an atomic connection counter and a handle to the database (pointer). When the counter increases from 0 to 1 the database is opened; when it drops from 1 to 0 the database is closed. As of `sqlite_orm` v1.10, this process is always synchronized (i.e., atomic). For the slow path that involves opening the database, the process of establishing a connection is performed under a mutually exclusive lock. When a connection already exists, the fast path can be taken, which only involves atomic reference counting.
29+
30+
### Special considerations
31+
32+
As mentioned above, a `storage` instance may be shared across threads to concurrently perform queries or CRUD operations.
33+
34+
However, there are things you should only do from a single-threaded context, preferably at the beginning of your program:
35+
36+
1. Synchronization of the database schema.
37+
2. Actions for which `sqlite_orm` must keep state independent of whether a database connection exists:
38+
a. Creation or deletion of application-defined scalar, aggregate, and collating functions with `storage.create_scalar_function()`, `storage.delete_scalar_function()`, `storage.create_aggregate_function()`, `storage.delete_aggregate_function()`, `storage.create_quoted_scalar_function()`, `storage.delete_quoted_scalar_function()`, `storage.create_collate_function()`, and `storage.delete_collate_function()`.
39+
b. Configuration of database limits with `storage.limit.set()`.
40+
c. Installation of the busy handler with `storage.busy_handler()`.
41+
d. Installation of an 'on open' handler with `storage.on_open`.
42+
e. Opening a permanent connection to a database on disk by calling `storage.open_forever()`.
43+
44+
## A word about performance
45+
46+
Opening and setting up a database connection has nontrivial overhead.
47+
48+
If your program accesses the database frequently, especially from multiple threads, open the database "forever" — preferably by passing connection control options when creating the `storage` instance:
49+
```
50+
auto storage = make_storage("", connection_control{true});
51+
```
52+
... or call:
53+
```
54+
storage.open_forever();
55+
```
56+
57+
Advantages:
58+
1. No locking is required in the fast path, which helps the CPU branch predictor.
59+
2. Connection setup (for example, configuring limits or registering application-defined functions) happens only once.

0 commit comments

Comments
 (0)