Skip to content

Commit 81ba551

Browse files
committed
Isolate PostgreSQL persistence from the node runtime
Co-Authored-By: HAL 9000
1 parent 0359c22 commit 81ba551

1 file changed

Lines changed: 95 additions & 60 deletions

File tree

src/io/postgres_store/mod.rs

Lines changed: 95 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//! Objects related to [`PostgresStore`] live here.
99
use std::collections::HashMap;
1010
use std::future::Future;
11-
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
11+
use std::sync::atomic::{AtomicU64, Ordering};
1212
use std::sync::{Arc, Mutex};
1313

1414
use lightning::io;
@@ -22,6 +22,7 @@ use tokio_postgres::{Config, Error as PgError};
2222
use self::pool::{make_config_connection, ClientConnection, PgTlsConnector, SmallPool};
2323
use crate::io::utils::check_namespace_key_validity;
2424
use crate::logger::{log_debug, log_info, LdkLogger, Logger};
25+
use crate::runtime::StoreRuntime;
2526

2627
mod migrations;
2728
mod pool;
@@ -101,8 +102,8 @@ pub struct PostgresStore {
101102
// operations aren't sensitive to the order of execution.
102103
next_write_version: AtomicU64,
103104

104-
// A store-internal runtime used for setup and connection driver tasks.
105-
internal_runtime: Option<tokio::runtime::Runtime>,
105+
// A store-internal runtime that drives PostgreSQL I/O independently from the node runtime.
106+
internal_runtime: Option<Arc<StoreRuntime>>,
106107
}
107108

108109
// tokio::sync::Mutex (used for the DB client) contains UnsafeCell which opts out of
@@ -145,30 +146,18 @@ impl PostgresStore {
145146
connection_string: String, db_name: Option<String>, kv_table_name: Option<String>,
146147
certificate_pem: Option<String>, logger: Option<Arc<Logger>>,
147148
) -> io::Result<Self> {
148-
let internal_runtime = tokio::runtime::Builder::new_multi_thread()
149-
.enable_all()
150-
.thread_name_fn(|| {
151-
static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0);
152-
let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst);
153-
format!("ldk-node-postgres-runtime-{}", id)
154-
})
155-
.worker_threads(INTERNAL_RUNTIME_WORKERS)
156-
.max_blocking_threads(INTERNAL_RUNTIME_WORKERS)
157-
.build()
158-
.map_err(|e| {
159-
io::Error::new(
160-
io::ErrorKind::Other,
161-
format!("Failed to build PostgreSQL runtime: {e}"),
162-
)
163-
})?;
149+
let internal_runtime = Arc::new(StoreRuntime::new(
150+
"ldk-node-postgres-runtime",
151+
INTERNAL_RUNTIME_WORKERS,
152+
"PostgreSQL",
153+
)?);
164154
let tls = Self::build_tls_connector(certificate_pem)?;
165-
let runtime_handle = internal_runtime.handle();
166-
let inner = tokio::task::block_in_place(|| {
167-
runtime_handle.block_on(async {
168-
PostgresStoreInner::new(connection_string, db_name, kv_table_name, tls, logger)
169-
.await
170-
})
171-
})?;
155+
let task = internal_runtime.spawn(async move {
156+
PostgresStoreInner::new(connection_string, db_name, kv_table_name, tls, logger).await
157+
});
158+
let inner = task.await.map_err(|e| {
159+
io::Error::new(io::ErrorKind::Other, format!("PostgreSQL runtime task failed: {}", e))
160+
})??;
172161
let inner = Arc::new(inner);
173162
let next_write_version = AtomicU64::new(1);
174163
Ok(Self { inner, next_write_version, internal_runtime: Some(internal_runtime) })
@@ -214,12 +203,18 @@ impl PostgresStore {
214203

215204
(inner_lock_ref, version)
216205
}
206+
207+
fn internal_runtime(&self) -> Arc<StoreRuntime> {
208+
Arc::clone(self.internal_runtime.as_ref().expect("PostgreSQL runtime must be available"))
209+
}
217210
}
218211

219212
impl Drop for PostgresStore {
220213
fn drop(&mut self) {
221214
if let Some(internal_runtime) = self.internal_runtime.take() {
222-
internal_runtime.shutdown_background();
215+
if let Ok(internal_runtime) = Arc::try_unwrap(internal_runtime) {
216+
internal_runtime.shutdown_background();
217+
}
223218
}
224219
}
225220
}
@@ -232,7 +227,18 @@ impl KVStore for PostgresStore {
232227
let secondary_namespace = secondary_namespace.to_string();
233228
let key = key.to_string();
234229
let inner = Arc::clone(&self.inner);
235-
async move { inner.read_internal(&primary_namespace, &secondary_namespace, &key).await }
230+
let runtime = self.internal_runtime();
231+
async move {
232+
let task = runtime.spawn(async move {
233+
inner.read_internal(&primary_namespace, &secondary_namespace, &key).await
234+
});
235+
task.await.map_err(|e| {
236+
io::Error::new(
237+
io::ErrorKind::Other,
238+
format!("PostgreSQL runtime task failed: {}", e),
239+
)
240+
})?
241+
}
236242
}
237243

238244
fn write(
@@ -244,18 +250,27 @@ impl KVStore for PostgresStore {
244250
let secondary_namespace = secondary_namespace.to_string();
245251
let key = key.to_string();
246252
let inner = Arc::clone(&self.inner);
253+
let runtime = self.internal_runtime();
247254
async move {
248-
inner
249-
.write_internal(
250-
inner_lock_ref,
251-
locking_key,
252-
version,
253-
&primary_namespace,
254-
&secondary_namespace,
255-
&key,
256-
buf,
255+
let task = runtime.spawn(async move {
256+
inner
257+
.write_internal(
258+
inner_lock_ref,
259+
locking_key,
260+
version,
261+
&primary_namespace,
262+
&secondary_namespace,
263+
&key,
264+
buf,
265+
)
266+
.await
267+
});
268+
task.await.map_err(|e| {
269+
io::Error::new(
270+
io::ErrorKind::Other,
271+
format!("PostgreSQL runtime task failed: {}", e),
257272
)
258-
.await
273+
})?
259274
}
260275
}
261276

@@ -268,17 +283,26 @@ impl KVStore for PostgresStore {
268283
let secondary_namespace = secondary_namespace.to_string();
269284
let key = key.to_string();
270285
let inner = Arc::clone(&self.inner);
286+
let runtime = self.internal_runtime();
271287
async move {
272-
inner
273-
.remove_internal(
274-
inner_lock_ref,
275-
locking_key,
276-
version,
277-
&primary_namespace,
278-
&secondary_namespace,
279-
&key,
288+
let task = runtime.spawn(async move {
289+
inner
290+
.remove_internal(
291+
inner_lock_ref,
292+
locking_key,
293+
version,
294+
&primary_namespace,
295+
&secondary_namespace,
296+
&key,
297+
)
298+
.await
299+
});
300+
task.await.map_err(|e| {
301+
io::Error::new(
302+
io::ErrorKind::Other,
303+
format!("PostgreSQL runtime task failed: {}", e),
280304
)
281-
.await
305+
})?
282306
}
283307
}
284308

@@ -288,16 +312,18 @@ impl KVStore for PostgresStore {
288312
let primary_namespace = primary_namespace.to_string();
289313
let secondary_namespace = secondary_namespace.to_string();
290314
let inner = Arc::clone(&self.inner);
291-
async move { inner.list_internal(&primary_namespace, &secondary_namespace).await }
292-
}
293-
}
294-
295-
impl PostgresStore {
296-
fn internal_runtime(&self) -> io::Result<&tokio::runtime::Runtime> {
297-
self.internal_runtime.as_ref().ok_or_else(|| {
298-
debug_assert!(false, "Failed to access internal PostgreSQL runtime");
299-
io::Error::new(io::ErrorKind::Other, "Failed to access internal PostgreSQL runtime")
300-
})
315+
let runtime = self.internal_runtime();
316+
async move {
317+
let task = runtime.spawn(async move {
318+
inner.list_internal(&primary_namespace, &secondary_namespace).await
319+
});
320+
task.await.map_err(|e| {
321+
io::Error::new(
322+
io::ErrorKind::Other,
323+
format!("PostgreSQL runtime task failed: {}", e),
324+
)
325+
})?
326+
}
301327
}
302328
}
303329

@@ -308,10 +334,19 @@ impl PaginatedKVStore for PostgresStore {
308334
let primary_namespace = primary_namespace.to_string();
309335
let secondary_namespace = secondary_namespace.to_string();
310336
let inner = Arc::clone(&self.inner);
337+
let runtime = self.internal_runtime();
311338
async move {
312-
inner
313-
.list_paginated_internal(&primary_namespace, &secondary_namespace, page_token)
314-
.await
339+
let task = runtime.spawn(async move {
340+
inner
341+
.list_paginated_internal(&primary_namespace, &secondary_namespace, page_token)
342+
.await
343+
});
344+
task.await.map_err(|e| {
345+
io::Error::new(
346+
io::ErrorKind::Other,
347+
format!("PostgreSQL runtime task failed: {}", e),
348+
)
349+
})?
315350
}
316351
}
317352
}

0 commit comments

Comments
 (0)