1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright 2025 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! An implementation of `deadpool` for `rusqlite`.
//!
//! Initially, we were using `deadpool-sqlite`, that is also using `rusqlite` as
//! the SQLite interface. However, in the implementation of
//! [`deadpool::managed::Manager`], when recycling an object (i.e. an SQLite
//! connection), [a SQL query is run to detect whether the connection is still
//! alive][connection-test]. It creates performance issues:
//!
//! 1. It runs a prepared SQL query, which has a non-negligle cost. Imagine each
//! connection is used to run on average one query; when recycled, a second
//! query was constantly run. Even if it's a simple query, it requires to
//! prepare a statement, to run and to query it.
//! 2. The SQL query was run in a blocking task. Indeed,
//! `deadpool_runtime::spawn_blocking` is used (via
//! `deadpool_sync::SyncWrapper::interact`), which includes [blocking the
//! thread, acquiring a lock][spawn_blocking] etc. All this has more
//! performance cost.
//!
//! Measures have shown it is a performance bottleneck for us, especially on
//! Android. Why specifically on Android and not other systems? This is still
//! unclear at the time of writing (2025-11-11), despites having spent several
//! days digging and trying to find an answer to this question.
//!
//! We have tried to use another approach to test the aliveness of the
//! connections without running queries. It has involved patching `rusqlite` to
//! add more bindings to SQLite, and patching `deadpool` itself, but without any
//! successful results.
//!
//! Finally, we have started questioning the reason of this test: why testing
//! whether the connection was still alive? After all, there is no reason a
//! connection should die in our case:
//!
//! - all connections are local,
//! - all interactions are behind [WAL], which is local only,
//! - even if for an unknown reason the connection died, using it next time
//! would create an error… exactly what would happen when recycling the
//! connection.
//!
//! Consequently, we have created a new implementation of `deadpool` for
//! `rusqlite` that doesn't test the aliveness of the connections when recycled.
//! We assume they are all alive.
//!
//! This implementation is, at the time of writing (2025-11-11):
//!
//! - 3.5 times faster on Android than `deadpool-sqlite`, removing the lock and
//! thread contention entirely,
//! - 2 times faster on iOS.
//!
//! [connection-test]: https://github.com/deadpool-rs/deadpool/blob/d6f7d58756f0cc7bdd1f3d54d820c1332d67e4d5/crates/deadpool-sqlite/src/lib.rs#L80-L100
//! [spawn_blocking]: https://github.com/deadpool-rs/deadpool/blob/d6f7d58756f0cc7bdd1f3d54d820c1332d67e4d5/crates/deadpool-sync/src/lib.rs#L113-L131
//! [WAL]: https://www.sqlite.org/wal.html
use ;
pub use *;
use ;
use SyncWrapper;
/// The default runtime used by `matrix-sdk-sqlite` for `deadpool`.
pub const RUNTIME: Runtime = Tokio1;
managed_reexports!;
/// Type representing a connection to SQLite from the [`Pool`].
pub type Connection = Object;
/// [`Manager`][managed::Manager] for creating and recycling SQLite
/// [`Connection`]s.