Skip to content

Move serve/stop_serving interface from PyDatabase to NeugDB #764

Description

@zhanglei1949

Background

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 localhost127.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

  1. 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.
  2. Ownership is inconsistent with the invariant. After Multiple NeugDBService instances can attach to one NeugDB, causing concurrent modification of shared db state #757 / refactor: Hold service pointer in database instance #763, NeugDB tracks its associated NeugDBService (RegisterService/UnregisterService, fail-fast Close()), 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.
  3. Duplicated policy. Rules like the thread_num clamp and the macOS localhost workaround risk diverging across callers.

Proposal

Move the serving lifecycle into NeugDB (guarded by BUILD_HTTP_SERVER), e.g.:

// include/neug/main/neug_db.h
#ifdef BUILD_HTTP_SERVER
std::string Serve(const ServiceConfig& config, bool blocking = false);
void StopServing();
bool IsServing() const;
#endif
  • 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.

Metadata

Metadata

Assignees

Labels

clientClient bindingscompilerCompiler infrastructure

Type

Projects

Status
In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions