Skip to content

Commit 3fa778c

Browse files
committed
Implement MigratableKVStore for postgres
This was unimplemented for the postgres kv store. Useful if the user wants to migrate to a different database and also in tests so we don't have to re-init and setup a node. AI-assisted-by: OpenAI Codex
1 parent 2bcd421 commit 3fa778c

1 file changed

Lines changed: 63 additions & 1 deletion

File tree

src/io/postgres_store/mod.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use std::sync::atomic::{AtomicU64, Ordering};
1212
use std::sync::{Arc, Mutex};
1313

1414
use lightning::io;
15-
use lightning::util::persist::{KVStore, PageToken, PaginatedKVStore, PaginatedListResponse};
15+
use lightning::util::persist::{
16+
KVStore, MigratableKVStore, PageToken, PaginatedKVStore, PaginatedListResponse,
17+
};
1618
use lightning_types::string::PrintableString;
1719
use native_tls::TlsConnector;
1820
use postgres_native_tls::MakeTlsConnector;
@@ -351,6 +353,24 @@ impl PaginatedKVStore for PostgresStore {
351353
}
352354
}
353355

356+
impl MigratableKVStore for PostgresStore {
357+
fn list_all_keys(
358+
&self,
359+
) -> impl Future<Output = Result<Vec<(String, String, String)>, io::Error>> + 'static + Send {
360+
let inner = Arc::clone(&self.inner);
361+
let runtime = self.internal_runtime();
362+
async move {
363+
let task = runtime.spawn(async move { inner.list_all_keys_internal().await });
364+
task.await.map_err(|e| {
365+
io::Error::new(
366+
io::ErrorKind::Other,
367+
format!("PostgreSQL runtime task failed: {}", e),
368+
)
369+
})?
370+
}
371+
}
372+
}
373+
354374
struct PostgresStoreInner {
355375
pool: SmallPool,
356376
config: Config,
@@ -725,6 +745,25 @@ impl PostgresStoreInner {
725745
Ok(keys)
726746
}
727747

748+
async fn list_all_keys_internal(&self) -> io::Result<Vec<(String, String, String)>> {
749+
let sql = format!(
750+
"SELECT primary_namespace, secondary_namespace, key FROM {}",
751+
self.kv_table_name_sql
752+
);
753+
754+
let err_map = |e: PgError| {
755+
let msg = format!("Failed to retrieve queried rows: {e}");
756+
io::Error::new(io::ErrorKind::Other, msg)
757+
};
758+
759+
let mut locked = self.locked_client().await?;
760+
let rows = query_with_retry!(self, locked, err_map, locked.query(sql.as_str(), &[]))?;
761+
762+
let keys: Vec<(String, String, String)> =
763+
rows.iter().map(|row| (row.get(0), row.get(1), row.get(2))).collect();
764+
Ok(keys)
765+
}
766+
728767
async fn list_paginated_internal(
729768
&self, primary_namespace: &str, secondary_namespace: &str, page_token: Option<PageToken>,
730769
) -> io::Result<PaginatedListResponse> {
@@ -904,6 +943,29 @@ mod tests {
904943
cleanup_store(&store_1).await;
905944
}
906945

946+
#[tokio::test(flavor = "multi_thread")]
947+
async fn test_postgres_store_list_all_keys() {
948+
let store = create_test_store("test_pg_list_all_keys").await;
949+
950+
KVStore::write(&store, "ns_a", "sub_a", "key_a", vec![1u8]).await.unwrap();
951+
KVStore::write(&store, "ns_a", "sub_b", "key_b", vec![2u8]).await.unwrap();
952+
KVStore::write(&store, "ns_b", "", "key_c", vec![3u8]).await.unwrap();
953+
954+
let mut keys = MigratableKVStore::list_all_keys(&store).await.unwrap();
955+
keys.sort();
956+
957+
assert_eq!(
958+
keys,
959+
vec![
960+
("ns_a".to_string(), "sub_a".to_string(), "key_a".to_string()),
961+
("ns_a".to_string(), "sub_b".to_string(), "key_b".to_string()),
962+
("ns_b".to_string(), "".to_string(), "key_c".to_string()),
963+
]
964+
);
965+
966+
cleanup_store(&store).await;
967+
}
968+
907969
async fn kill_connection(store: &PostgresStore) {
908970
// Terminate every backend in the pool so the next op deterministically
909971
// hits a closed connection regardless of which slot `get` selects.

0 commit comments

Comments
 (0)