• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tari-project / tari / 18097567115

29 Sep 2025 12:50PM UTC coverage: 58.554% (-2.3%) from 60.88%
18097567115

push

github

web-flow
chore(ci): switch rust toolchain to stable (#7524)

Description
switch rust toolchain to stable

Motivation and Context
use stable rust toolchain


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Standardized Rust toolchain on stable across CI workflows for more
predictable builds.
* Streamlined setup by removing unnecessary components and aligning
toolchain configuration with environment variables.
  * Enabled an environment flag to improve rustup behavior during CI.
* Improved coverage workflow consistency with dynamic toolchain
selection.

* **Tests**
* Removed nightly-only requirements, simplifying test commands and
improving compatibility.
* Expanded CI triggers to include ci-* branches for better pre-merge
validation.
* Maintained existing job logic while improving reliability and
maintainability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

66336 of 113291 relevant lines covered (58.55%)

551641.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

52.69
/common_sqlite/src/sqlite_connection_pool.rs
1
// Copyright 2020. The Tari Project
2
//
3
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4
// following conditions are met:
5
//
6
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7
// disclaimer.
8
//
9
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10
// following disclaimer in the documentation and/or other materials provided with the distribution.
11
//
12
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13
// products derived from this software without specific prior written permission.
14
//
15
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22

23
use core::time::Duration;
24
use std::{convert::TryFrom, fmt::Display, path::PathBuf};
25

26
use diesel::{
27
    r2d2::{ConnectionManager, Pool, PooledConnection},
28
    SqliteConnection,
29
};
30
use log::*;
31

32
use crate::{connection_options::ConnectionOptions, error::SqliteStorageError};
33

34
const LOG_TARGET: &str = "common_sqlite::sqlite_connection_pool";
35

36
/// The default timeout for acquiring an R2D2 pool connection over and above the PRAGMA busy timeout.
37
pub const R2D2_POOL_CONNECTION_DELTA: Duration = Duration::from_secs(5);
38
/// The default timeout for acquiring an R2D2 pool connection.
39
/// Note: The default R2D2 connection timeout is 30s.
40
pub const R2D2_POOL_CONNECTION_TIMEOUT: Duration = Duration::from_secs(30);
41

42
/// Thin wrapper around an r2d2 `Pool<SqliteConnection>` with standard SQLite
43
/// configuration (WAL, FKs, busy timeout) applied to each connection.
44
///
45
/// The pool is created lazily by calling [`create_pool`] once. After that,
46
/// callers can obtain connections via [`get_pooled_connection`], a timed
47
/// variant, or a non-blocking `try_get_pooled_connection`.
48
///
49
/// # Concurrency notes
50
/// SQLite allows only a single writer at a time (even in WAL mode). Keep write
51
/// transactions short and consider limiting concurrent writers at the
52
/// application layer (e.g. a semaphore) to reduce lock contention.
53
///
54
/// # Timeout interplay
55
/// The r2d2 **pool checkout** timeout is configured via
56
/// `Pool::builder().connection_timeout(...)` inside [`create_pool`]. This is
57
/// separate from SQLite’s `PRAGMA busy_timeout` set by [`ConnectionOptions`].
58
/// Prefer `connection_timeout >= busy_timeout` (plus a little headroom) to
59
/// avoid premature pool timeouts while connections are waiting on SQLite locks.
60
#[derive(Clone)]
61
pub struct SqliteConnectionPool {
62
    /// The underlying r2d2 pool. `None` until [`create_pool`] is called.
63
    pool: Option<Pool<ConnectionManager<SqliteConnection>>>,
64
    /// Database path / connection string (`:memory:`, filesystem path, or
65
    /// `file:NAME?mode=memory&cache=shared`).
66
    db_path: String,
67
    /// Maximum number of concurrently open connections managed by the pool.
68
    pool_size: usize,
69
    /// Per-connection SQLite options applied on acquisition.
70
    connection_options: ConnectionOptions,
71
}
72

73
impl SqliteConnectionPool {
74
    /// Create a wrapper with the given target database, pool size and
75
    /// connection options (WAL, FKs, busy timeout). The r2d2 pool is not built
76
    /// until [`create_pool`] is called.
77
    pub fn new(
331✔
78
        db_path: String,
331✔
79
        pool_size: usize,
331✔
80
        enable_wal: bool,
331✔
81
        enable_foreign_keys: bool,
331✔
82
        busy_timeout: Duration,
331✔
83
    ) -> Self {
331✔
84
        Self {
331✔
85
            pool: None,
331✔
86
            db_path,
331✔
87
            pool_size,
331✔
88
            connection_options: ConnectionOptions::new(enable_wal, enable_foreign_keys, busy_timeout),
331✔
89
        }
331✔
90
    }
331✔
91

92
    /// Create an sqlite connection pool managed by the pool connection manager
93
    pub fn create_pool(&mut self) -> Result<(), SqliteStorageError> {
331✔
94
        if self.pool.is_none() {
331✔
95
            let mut builder = Pool::builder()
331✔
96
                .max_size(u32::try_from(self.pool_size)?)
331✔
97
                .connection_customizer(Box::new(self.connection_options.clone()));
331✔
98
            if let Some(timeout) = self.connection_options.get_busy_timeout() {
331✔
99
                // When we get a pooled connection, we want the pool connection timeout to be longer
331✔
100
                // than the database busy timeout (set by `PRAGMA busy_timeout`). Here we set the
331✔
101
                // connection timeout to whatever the busy timeout is plus a delta (5s).
331✔
102
                builder = builder.connection_timeout(timeout + R2D2_POOL_CONNECTION_DELTA);
331✔
103
            } else {
331✔
104
                // If no busy timeout is set, we use the default value.
×
105
                builder = builder.connection_timeout(R2D2_POOL_CONNECTION_TIMEOUT);
×
106
            }
×
107
            let pool = builder
331✔
108
                .build(ConnectionManager::<SqliteConnection>::new(self.db_path.as_str()))
331✔
109
                .map_err(|e| SqliteStorageError::DieselR2d2Error(e.to_string()))?;
331✔
110
            self.pool = Some(pool);
331✔
111
        } else {
112
            warn!(target: LOG_TARGET, "Connection pool for {} already exists", self.db_path);
×
113
        }
114
        Ok(())
331✔
115
    }
331✔
116

117
    /// Return a pooled sqlite connection managed by the pool connection manager, waits for at most the configured
118
    /// connection timeout before returning an error.
119
    pub fn get_pooled_connection(
12,832✔
120
        &self,
12,832✔
121
    ) -> Result<PooledConnection<ConnectionManager<SqliteConnection>>, SqliteStorageError> {
12,832✔
122
        if let Some(pool) = self.pool.as_ref() {
12,832✔
123
            let start = std::time::Instant::now();
12,832✔
124
            let connection = pool.get().map_err(|e| {
12,832✔
125
                warn!(target: LOG_TARGET, "Connection pool state {:?}: {}", pool.state(), e);
×
126
                SqliteStorageError::DieselR2d2Error(e.to_string())
×
127
            });
×
128
            trace!(target: LOG_TARGET, "Acquired 'get_pooled_connection' from pool in {:.2?}", start.elapsed());
12,832✔
129
            connection
12,832✔
130
        } else {
131
            Err(SqliteStorageError::DieselR2d2Error("Pool does not exist".to_string()))
×
132
        }
133
    }
12,832✔
134

135
    /// Return a pooled sqlite connection managed by the pool connection manager, waits for at most supplied
136
    /// connection timeout before returning an error.
137
    pub fn get_pooled_connection_timeout(
×
138
        &self,
×
139
        timeout: Duration,
×
140
    ) -> Result<PooledConnection<ConnectionManager<SqliteConnection>>, SqliteStorageError> {
×
141
        if let Some(pool) = self.pool.clone() {
×
142
            let start = std::time::Instant::now();
×
143
            let connection = pool.get_timeout(timeout).map_err(|e| {
×
144
                warn!(target: LOG_TARGET, "Connection pool state {:?}: {}", pool.state(), e);
×
145
                SqliteStorageError::DieselR2d2Error(e.to_string())
×
146
            });
×
147
            trace!(target: LOG_TARGET, "Acquired 'get_pooled_connection_timeout' from pool in {:.2?}", start.elapsed());
×
148
            connection
×
149
        } else {
150
            Err(SqliteStorageError::DieselR2d2Error("Pool does not exist".to_string()))
×
151
        }
152
    }
×
153

154
    /// Return a pooled sqlite connection managed by the pool connection manager, returns None if there are no idle
155
    /// connections available in the pool. This method will not block waiting to establish a new connection.
156
    pub fn try_get_pooled_connection(
×
157
        &self,
×
158
    ) -> Result<Option<PooledConnection<ConnectionManager<SqliteConnection>>>, SqliteStorageError> {
×
159
        if let Some(pool) = self.pool.clone() {
×
160
            let start = std::time::Instant::now();
×
161
            let connection = pool.try_get();
×
162
            if connection.is_none() {
×
163
                warn!(target: LOG_TARGET, "No connections available, pool state {:?}", pool.state());
×
164
            } else {
165
                trace!(target: LOG_TARGET, "Acquired 'try_get_pooled_connection' from pool in {:.2?}", start.elapsed());
×
166
            }
167
            Ok(connection)
×
168
        } else {
169
            Err(SqliteStorageError::DieselR2d2Error("Pool does not exist".to_string()))
×
170
        }
171
    }
×
172

173
    /// Returns the database path / connection string used by this pool.
174
    pub fn db_path(&self) -> PathBuf {
2,520✔
175
        PathBuf::from(&self.db_path)
2,520✔
176
    }
2,520✔
177

178
    /// Perform cleanup on the connection pool. This will drop the pool and return the state of the pool.
179
    pub fn cleanup(&mut self) -> Option<String> {
189✔
180
        if let Some(pool) = self.pool.take() {
189✔
181
            let state = format!("{:?}", pool.state());
189✔
182
            drop(pool);
189✔
183
            return Some(state);
189✔
184
        }
×
185
        None
×
186
    }
189✔
187
}
188

189
impl Display for SqliteConnectionPool {
190
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
191
        let pool_state = if let Some(pool) = self.pool.clone() {
×
192
            format!("{:?}", pool.state())
×
193
        } else {
194
            "None".to_string()
×
195
        };
196
        write!(
×
197
            f,
×
198
            "SqliteConnectionPool {{ pool state: {}, db_path: {}, pool_size: {}, connection_options: {:?} }}",
×
199
            pool_state, self.db_path, self.pool_size, self.connection_options
200
        )
201
    }
×
202
}
203

204
/// Helper trait for components that need a pooled SQLite connection.
205
pub trait PooledDbConnection: Send + Sync + Clone {
206
    /// Acquire a pooled connection, or return an error if the pool is
207
    /// unavailable or the checkout times out.
208
    type Error;
209

210
    fn get_pooled_connection(&self) -> Result<PooledConnection<ConnectionManager<SqliteConnection>>, Self::Error>;
211
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc