You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The serve() / stop_serving() interface currently lives entirely in PyDatabase (the pybind11 wrapper under tools/python_bind/src/py_database.cc). PyDatabase directly owns a std::unique_ptr<NeugDBService> and implements the whole serving lifecycle itself:
thread_num validation against database->config().max_thread_num
database->PrepareForServing()
ServiceConfig assembly, including the macOS localhost → 127.0.0.1 workaround
NeugDBService construction, Start() / run_and_wait_for_exit() / Stop(), and cleanup on exceptions
PyDatabase is supposed to be a thin wrapper around the C++ API. Serving is core database functionality, not binding-specific logic.
Problem
Core logic is trapped in the binding layer. Any other consumer of the C++ API (the Node.js/Rust bindings, bin/rt_server.cc, bin/benchmark.cc, embedded C++ users) has to re-implement the same sequence — and can get it wrong: wrong call ordering, missing validation, leaking the service when Start() throws, etc.
NeugDB owns the service it creates (e.g. an internal std::unique_ptr<NeugDBService>), reusing the existing active_service_ / RegisterService machinery so owned and externally-constructed services share the same single-service invariant.
PyDatabase::serve / stop_serving become thin forwards; validation and platform workarounds move down into NeugDB::Serve so all callers share them.
Close() semantics with a db-owned service need an explicit decision: keep fail-fast (current behavior for external services) vs. let Close() stop a service it owns. External (non-owned) services must keep the fail-fast behavior.
Python-facing behavior must stay compatible: same signatures, same error behavior, same blocking semantics (blocking=true blocks until shutdown).
rt_server / benchmark may keep constructing NeugDBService directly (out of scope) or be migrated as a follow-up.
Acceptance criteria
NeugDB exposes serve/stop API when built with BUILD_HTTP_SERVER=ON; without it, calling serve fails with a clear error.
PyDatabase delegates to NeugDB; all existing Python tests using db.serve(...) pass unmodified (test_tp_service.py, test_db_connection.py, test_db_init.py, ...).
New C++ unit tests in tests/unittest/test_db_svc.cc covering: serve twice → error; serve on closed db → error; stop idempotency; Close() vs. owned service per the decided semantics.
clang-format / cpplint clean; no behavior change in rt_server.
Background
The
serve()/stop_serving()interface currently lives entirely inPyDatabase(the pybind11 wrapper undertools/python_bind/src/py_database.cc).PyDatabasedirectly owns astd::unique_ptr<NeugDBService>and implements the whole serving lifecycle itself:thread_numvalidation againstdatabase->config().max_thread_numdatabase->PrepareForServing()ServiceConfigassembly, including the macOSlocalhost→127.0.0.1workaroundNeugDBServiceconstruction,Start()/run_and_wait_for_exit()/Stop(), and cleanup on exceptionsPyDatabaseis supposed to be a thin wrapper around the C++ API. Serving is core database functionality, not binding-specific logic.Problem
bin/rt_server.cc,bin/benchmark.cc, embedded C++ users) has to re-implement the same sequence — and can get it wrong: wrong call ordering, missing validation, leaking the service whenStart()throws, etc.NeugDBtracks its associatedNeugDBService(RegisterService/UnregisterService, fail-fastClose()), so the db enforces "at most one service" — yet the service's owning object still lives outside the db in every caller. The invariant is enforced by the db but the lifecycle is not owned by the db.thread_numclamp and the macOSlocalhostworkaround risk diverging across callers.Proposal
Move the serving lifecycle into
NeugDB(guarded byBUILD_HTTP_SERVER), e.g.:NeugDBowns the service it creates (e.g. an internalstd::unique_ptr<NeugDBService>), reusing the existingactive_service_/RegisterServicemachinery so owned and externally-constructed services share the same single-service invariant.PyDatabase::serve/stop_servingbecome thin forwards; validation and platform workarounds move down intoNeugDB::Serveso all callers share them.Close()semantics with a db-owned service need an explicit decision: keep fail-fast (current behavior for external services) vs. letClose()stop a service it owns. External (non-owned) services must keep the fail-fast behavior.blocking=trueblocks until shutdown).rt_server/benchmarkmay keep constructingNeugDBServicedirectly (out of scope) or be migrated as a follow-up.Acceptance criteria
NeugDBexposes serve/stop API when built withBUILD_HTTP_SERVER=ON; without it, calling serve fails with a clear error.PyDatabasedelegates toNeugDB; all existing Python tests usingdb.serve(...)pass unmodified (test_tp_service.py,test_db_connection.py,test_db_init.py, ...).tests/unittest/test_db_svc.cccovering: serve twice → error; serve on closed db → error; stop idempotency;Close()vs. owned service per the decided semantics.rt_server.