NeoGraph 0.16.0
A C++20 Graph Agent Engine Library — LangGraph for C++
Loading...
Searching...
No Matches
postgres_checkpoint.h
Go to the documentation of this file.
1
77#pragma once
78
80
81#include <atomic>
82#include <condition_variable>
83#include <memory>
84#include <mutex>
85#include <queue>
86#include <string>
87#include <utility>
88#include <vector>
89
90// Forward-declare libpq's opaque PGconn. Keeps libpq-fe.h out of
91// every translation unit that includes this header.
92struct pg_conn;
93
94namespace neograph::graph {
95
96namespace test_access {
97class PostgresCheckpointStoreTestAccess;
98}
99
103struct PgConn {
104 pg_conn* raw = nullptr;
105 PgConn() = default;
106 explicit PgConn(pg_conn* p) : raw(p) {}
107 ~PgConn();
108 PgConn(const PgConn&) = delete;
109 PgConn& operator=(const PgConn&) = delete;
110 PgConn(PgConn&&) = delete;
111 PgConn& operator=(PgConn&&) = delete;
112};
113
143class NEOGRAPH_API PostgresCheckpointStore : public CheckpointStore {
144public:
151 explicit PostgresCheckpointStore(const std::string& conn_str, size_t pool_size = 8);
152
157 size_t reconnect_count() const { return reconnect_count_; }
158
161 size_t pool_size() const { return pool_.size(); }
162
163 ~PostgresCheckpointStore() override;
164
165 // Non-copyable, non-movable — mutex would need rebinding on move
166 // and connection pool slots are keyed by stable indices.
168 PostgresCheckpointStore& operator=(const PostgresCheckpointStore&) = delete;
169
170 void save(const Checkpoint& cp) override;
171 std::optional<Checkpoint> load_latest(const std::string& thread_id) override;
172 std::optional<Checkpoint> load_by_id(const std::string& id) override;
173 std::vector<Checkpoint> list(const std::string& thread_id, int limit = 100) override;
174 void delete_thread(const std::string& thread_id) override;
175
176 void put_writes(const std::string& thread_id,
177 const std::string& parent_checkpoint_id,
178 const PendingWrite& write) override;
179 std::vector<PendingWrite> get_writes(const std::string& thread_id,
180 const std::string& parent_checkpoint_id) override;
181 void clear_writes(const std::string& thread_id,
182 const std::string& parent_checkpoint_id) override;
183
184 // ── Async peers (Sem 4 follow-up) ───────────────────────────────────
185 //
186 // True async: libpq nonblocking mode + asio::posix::stream_descriptor
187 // on PQsocket() + co_await on write/read readiness. The io_context
188 // stays free for other coroutines during PG's commit fsync, so N
189 // concurrent save_async calls across pool slots actually overlap
190 // instead of serialising on the main worker thread.
191 //
192 // Successful operations use the same storage schema and values as the
193 // sync peers. Async connection setup/replacement additionally follows
194 // the global deadline policy documented on this class.
195
196 asio::awaitable<void> save_async(const Checkpoint& cp) override;
197 asio::awaitable<std::optional<Checkpoint>> load_latest_async(
198 const std::string& thread_id) override;
199 asio::awaitable<std::optional<Checkpoint>> load_by_id_async(const std::string& id) override;
200 asio::awaitable<std::vector<Checkpoint>> list_async(const std::string& thread_id,
201 int limit = 100) override;
202 asio::awaitable<void> delete_thread_async(const std::string& thread_id) override;
203
204 asio::awaitable<void> put_writes_async(const std::string& thread_id,
205 const std::string& parent_checkpoint_id,
206 const PendingWrite& write) override;
207 asio::awaitable<std::vector<PendingWrite>> get_writes_async(
208 const std::string& thread_id, const std::string& parent_checkpoint_id) override;
209 asio::awaitable<void> clear_writes_async(const std::string& thread_id,
210 const std::string& parent_checkpoint_id) override;
211
213 bool requires_managed_budget(const std::string& thread_id) override;
214 asio::awaitable<bool> requires_managed_budget_async(std::string thread_id) override;
215
216 std::shared_ptr<OwnedManagedBudgetLease> acquire_managed_budget_lease(
217 const ManagedBudgetLeaseScope& scope, const std::string& expected_checkpoint_id,
218 const std::string& expected_checkpoint_commitment) override;
219 asio::awaitable<std::shared_ptr<OwnedManagedBudgetLease>> acquire_managed_budget_lease_async(
220 ManagedBudgetLeaseScope scope, std::string expected_checkpoint_id,
221 std::string expected_checkpoint_commitment) override;
222 ManagedBudgetEffectReceipt begin_managed_budget_effect(
223 const std::shared_ptr<OwnedManagedBudgetLease>& lease, const std::string& effect_id,
224 std::uint64_t exact_claim_amount, const std::string& prepared_request_digest) override;
225 asio::awaitable<ManagedBudgetEffectReceipt> begin_managed_budget_effect_async(
226 std::shared_ptr<OwnedManagedBudgetLease> lease, std::string effect_id,
227 std::uint64_t exact_claim_amount, std::string prepared_request_digest) override;
228 void settle_managed_budget_effect(const std::shared_ptr<OwnedManagedBudgetLease>& lease,
229 const ManagedBudgetEffectReceipt& effect, sp::runtime::Result genuine_outcome,
230 const UsageAccumulator::AuthoritySnapshot& authority) override;
231 asio::awaitable<void> settle_managed_budget_effect_async(
232 std::shared_ptr<OwnedManagedBudgetLease> lease, ManagedBudgetEffectReceipt effect,
233 sp::runtime::Result genuine_outcome, UsageAccumulator::AuthoritySnapshot authority) override;
234 void publish_managed_budget_checkpoint(
235 const std::shared_ptr<OwnedManagedBudgetLease>& lease, const Checkpoint& checkpoint) override;
236 asio::awaitable<void> publish_managed_budget_checkpoint_async(
237 std::shared_ptr<OwnedManagedBudgetLease> lease, Checkpoint checkpoint) override;
238 void release_managed_budget_lease(const std::shared_ptr<OwnedManagedBudgetLease>& lease) override;
239 asio::awaitable<void> release_managed_budget_lease_async(
240 std::shared_ptr<OwnedManagedBudgetLease> lease) override;
241
245
249 size_t blob_count();
250
251private:
252 // Constructs only the slot scheduler for deterministic pool tests.
253 // The friend test helper never calls a method that dereferences pool_.
254 struct PoolOnlyTag {};
255 explicit PostgresCheckpointStore(PoolOnlyTag, size_t pool_size);
256
257 void ensure_schema();
258
261 template <typename Fn>
262 auto with_conn(Fn&& fn);
263
273 template <typename Fn>
274 auto with_conn_async(Fn& fn) -> decltype(fn(std::declval<pg_conn*>()));
275
278 size_t acquire_slot();
279 asio::awaitable<size_t> acquire_slot_async();
280 void release_slot(size_t idx);
281 void rebuild_slot(size_t idx);
282 asio::awaitable<void> rebuild_slot_async(size_t idx);
283 size_t waiter_count_for_test();
284 static asio::awaitable<bool> wait_socket_either_for_test(int fd);
285 static void set_async_connection_test_seams(int poll_delay_ms, int timeout_ms);
286 static int async_connection_timeout_ms_for_test(const std::string& conn_str);
287
290 std::string conn_str_;
291
294 std::vector<std::unique_ptr<PgConn>> pool_;
295
299 std::queue<size_t> free_;
300 std::mutex pool_mutex_;
301 std::condition_variable pool_cv_; // Retained at its original ABI offset.
302
306 std::atomic<size_t> reconnect_count_{0};
307
308 friend class test_access::PostgresCheckpointStoreTestAccess;
309};
310
311} // namespace neograph::graph
Checkpoint system for graph execution state persistence and time-travel.
Abstract interface for checkpoint persistence backends.
Definition checkpoint.h:400
Actual write-ahead claim, bound to one original generation and owned actor.
Definition checkpoint.h:96
Persistent CheckpointStore backed by PostgreSQL via libpq.
PostgresCheckpointStore(const std::string &conn_str, size_t pool_size=8)
std::vector< PendingWrite > get_writes(const std::string &thread_id, const std::string &parent_checkpoint_id) override
Load all pending writes attached to a parent checkpoint.
size_t pool_size() const
Number of connections in the pool.
void save(const Checkpoint &cp) override
Save a checkpoint.
void clear_writes(const std::string &thread_id, const std::string &parent_checkpoint_id) override
Discard pending writes for a parent checkpoint once its successor super-step has been fully committed...
bool requires_managed_budget(const std::string &thread_id) override
Indexed denial obligation; does not authenticate or authorize a bank.
size_t reconnect_count() const
Number of times a pool slot's connection was replaced after a broken connection or an interrupted asy...
void delete_thread(const std::string &thread_id) override
Delete all checkpoints for a thread.
std::vector< Checkpoint > list(const std::string &thread_id, int limit=100) override
List checkpoints for a thread, most recently saved first, one entry per ID; the first entry is load_l...
void drop_schema()
Drop checkpoint data tables, retaining obligations, migration markers, currency heads and write-ahead...
std::optional< Checkpoint > load_by_id(const std::string &id) override
Load a checkpoint by its unique ID.
std::optional< Checkpoint > load_latest(const std::string &thread_id) override
Load the most recently saved checkpoint for a thread.
void put_writes(const std::string &thread_id, const std::string &parent_checkpoint_id, const PendingWrite &write) override
Record a successful node execution within an in-progress super-step.
size_t blob_count()
Test helper: count rows in neograph_checkpoint_blobs.
Serialized snapshot of graph execution state at a single super-step.
Definition checkpoint.h:173
Successful node writes recorded within an in-progress super-step.
Definition checkpoint.h:242
RAII wrapper around a libpq PGconn.