stopAndAwaitProxiedStateStore function
Stops the proxied store serving workspacePath and does not return until
it is GONE.
Order is the whole point. The proxy and its dolt sql-server hold the data
directory a delete removes; a delete that races them leaves a server
running over a path that no longer exists, and the next fixture store on
the host can bind to it and fail with a refusal that names neither.
The kill set is seeded from THREE accounts because none is complete alone:
recordedPids — what a verified identity already named — plus bd's own
PID artifacts under pidRootPath, which name a forked child a whole exec
before ps can recognise it, plus the live census, which is the only
account of the successor bd spawns to serve the very command that stopped
the last one. The census arm is wider than the store processes on purpose:
a store process killed while its client still runs simply comes back.
bd dolt stop is NOT the instrument. It is a MODE CHANGE — it migrates the
workspace back to embedded storage, which a CGO-free binary cannot even
open — and it re-spawns a proxy to serve its own command, so the process it
leaves behind is never the one it reported stopping.
LOUD on residue it cannot remove, via waitForProxiedStateStoreExit. Idempotent, so a registered teardown may follow a manual call.
Implementation
Future<void> stopAndAwaitProxiedStateStore({
required String workspacePath,
required String pidRootPath,
Set<int> recordedPids = const {},
}) async {
final named = <int>{
...recordedPids,
...proxiedStateStorePids(pidRootPath: pidRootPath),
};
// bd's own record is signalled FIRST, before any census: a forked child is
// in these files a whole exec before it is recognisable in `ps`.
for (final target in named) {
Process.killPid(target, ProcessSignal.sigkill);
}
await waitForProxiedStateStoreExit(
survivingPids: () => _reapStoreResidue(workspacePath, named),
heldLockPaths: () => _heldProxyLocks(pidRootPath),
now: DateTime.now,
delay: (duration) => Future<void>.delayed(duration),
);
}