You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+9-2Lines changed: 9 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,9 +2,16 @@
2
2
3
3
## next / unreleased
4
4
5
-
### Changed
5
+
### Fork safety improvements
6
+
7
+
Sqlite itself is [not fork-safe](https://www.sqlite.org/howtocorrupt.html#_carrying_an_open_database_connection_across_a_fork_). Specifically, writing in a child process to a database connection that was created in the parent process may corrupt the database file. To mitigate this risk, sqlite3-ruby has implemented the following changes:
8
+
9
+
- Open writable database connections carried across a `fork()` will immediately be closed in the child process to mitigate the risk of corrupting the database file.
10
+
- These connections will be incompletely closed ("discarded") which will result in a one-time memory leak in the child process.
11
+
12
+
If it's at all possible, we strongly recommend that you close writable database connections in the parent before forking.
6
13
7
-
- Any database connections carried across a `fork()` will not be fully closed to help protect database files against corruption. Using a database connection in a child process that was created in a parent process is unsafe and may corrupt the database file. If an inherited connection is closed then a warning will be emitted and some reserved memory will be lost to the child process permanently. See the README "Fork Safety" section and `adr/2024-09-fork-safety.md` for more information. [#558]@flavorjones
14
+
See the README "Fork Safety" section and `adr/2024-09-fork-safety.md` for more information. [#558]@flavorjones
and you should not carry an open database connection across a `fork()`. Using an inherited
156
+
and instructs users to not carry an open writable database connection across a `fork()`. Using an inherited
157
157
connection in the child may corrupt your database, leak memory, or cause other undefined behavior.
158
158
159
-
Instead, whenever possible, close connections in the parent before forking.
159
+
To help protect users of this gem from accidental corruption due to this lack of fork safety, the gem will immediately close any open writable databases in the child after a fork.
160
160
161
-
If that's not possible or convenient, then immediately close any inherited connections in the child
162
-
after forking, before opening any new connections. This will incur a small one-time memory leak per
163
-
connection, but that's preferable to potentially corrupting your database.
161
+
Whenever possible, close writable connections in the parent before forking. Discarding writable
162
+
connections in the child will incur a small one-time memory leak per connection, but that's
163
+
preferable to potentially corrupting your database.
164
164
165
165
See [./adr/2024-09-fork-safety.md](./adr/2024-09-fork-safety.md) for more information and context.
Copy file name to clipboardExpand all lines: adr/2024-09-fork-safety.md
+10-4Lines changed: 10 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
2
-
# 2024-09 Discard database connections when carried across fork()of fork safety
2
+
# 2024-09 Automatically close database connections when carried across fork()
3
3
4
4
## Status
5
5
@@ -15,17 +15,23 @@ SQLite is known to not be fork-safe[^howto], so this was not entirely surprising
15
15
Advice from upstream contributors[^advice] is, essentially: don't fork if you have open database connections. Or, if you have forked, don't call `sqlite3_close` on those connections and thereby leak some amount of memory in the child process. Neither of these options are ideal, see below.
16
16
17
17
18
-
## Decision
18
+
## Decisions
19
19
20
-
Open database connections carried across a `fork()` will not be fully closed in the child process, to avoid the risk of corrupting the database file.
20
+
1. Open writable database connections carried across a `fork()` will automatically be closed in the child process to mitigate the risk of corrupting the database file.
21
+
2. These connections will be incompletely closed ("discarded") which will result in a one-time memory leak in the child process.
21
22
22
-
The sqlite3-ruby gem will track the ID of the process that opened each database connection. If, when the database is closed (either explicitly with `Database#close` or implicitly via GC) the current process ID is different from the original process, then we "discard" the connection.
23
+
First, the gem will register an "after fork" handler via `Process._fork` that will close any open writable database connections in the child process. This is a best-effort attempt to avoid corruption, but it is not guaranteed to prevent corruption in all cases. Any connections closed by this handler will also emit a warning to let users know what's happening.
24
+
25
+
Second, the sqlite3-ruby gem will store the ID of the process that opened each database connection. If, when a writable database is closed (either explicitly with `Database#close` or implicitly via GC or after-fork callback) the current process ID is different from the original process, then we "discard" the connection.
23
26
24
27
"Discard" here means:
25
28
26
29
- The `Database` object acts "closed", including returning `true` from `#closed?`.
27
30
-`sqlite3_close_v2` is not called on the object, because it is unsafe to do so per sqlite instructions[^howto]. As a result, some memory will be lost permanently (a one-time "memory leak").
28
31
- Open file descriptors associated with the database are closed.
32
+
- Any memory that can be freed safely is recovered.
33
+
34
+
Note that readonly databases are being treated as "fork safe" and are not affected by any of these changes.
0 commit comments