2020 */
2121final class DocumentStore
2222{
23- /** @var int[] PIDs of outstanding async child processes. */
23+ /**
24+ * PIDs of outstanding async child processes, keyed by nodeId.
25+ * Keying by nodeId lets waitForNode() drain exactly one write without
26+ * blocking every other in-flight write.
27+ *
28+ * @var array<int, int> nodeId → PID
29+ */
2430 private array $ pendingPids = [];
2531
2632 public function __construct (private readonly string $ docsDir ) {}
@@ -59,8 +65,8 @@ public function write(
5965 $ this ->writeSync ($ nodeId , $ docId , $ text , $ metadata );
6066 exit (0 );
6167 } else {
62- // Parent: record PID and return.
63- $ this ->pendingPids [] = $ pid ;
68+ // Parent: record PID keyed by nodeId and return.
69+ $ this ->pendingPids [$ nodeId ] = $ pid ;
6470 return ;
6571 }
6672 }
@@ -69,6 +75,25 @@ public function write(
6975 $ this ->writeSync ($ nodeId , $ docId , $ text , $ metadata );
7076 }
7177
78+ /**
79+ * Block until the async write for a specific node has completed.
80+ *
81+ * Use this before deleting a node's file so a late child write cannot
82+ * recreate {nodeId}.bin after the unlink().
83+ */
84+ public function waitForNode (int $ nodeId ): void
85+ {
86+ if (!isset ($ this ->pendingPids [$ nodeId ])) {
87+ return ;
88+ }
89+
90+ if (function_exists ('pcntl_waitpid ' )) {
91+ pcntl_waitpid ($ this ->pendingPids [$ nodeId ], $ status );
92+ }
93+
94+ unset($ this ->pendingPids [$ nodeId ]);
95+ }
96+
7297 /**
7398 * Block until every outstanding async write has completed.
7499 * Must be called before index files are written (see VectorDatabase::save()).
0 commit comments