@@ -3,6 +3,7 @@ import * as fs from 'node:fs';
33import path from 'node:path' ;
44import process from 'node:process' ;
55import { threadId } from 'node:worker_threads' ;
6+ import { SHARDED_WAL_COORDINATOR_ID_ENV_VAR } from './profiler/constants.js' ;
67
78/**
89 * Codec for encoding/decoding values to/from strings for WAL storage.
@@ -356,6 +357,14 @@ export function setLeaderWal(envVarName: string, profilerID: string): void {
356357// eslint-disable-next-line functional/no-let
357358let shardCount = 0 ;
358359
360+ /**
361+ * Generates a unique sharded WAL ID based on performance time origin, process ID, thread ID, and instance count.
362+ */
363+ function getShardedWalId ( ) {
364+ // eslint-disable-next-line functional/immutable-data
365+ return `${ Math . round ( performance . timeOrigin ) } .${ process . pid } .${ threadId } .${ ++ ShardedWal . instanceCount } ` ;
366+ }
367+
359368/**
360369 * Generates a human-readable shard ID.
361370 * This ID is unique per process/thread/shard combination and used in the file name.
@@ -462,24 +471,52 @@ export function getShardedFinalPath<T extends object | string = object>(opt: {
462471 */
463472
464473export class ShardedWal < T extends object | string = object > {
474+ static instanceCount = 0 ;
475+ readonly #id: string = getShardedWalId ( ) ;
465476 readonly groupId = getShardedGroupId ( ) ;
466477 readonly #format: WalFormat < T > ;
467478 readonly #dir: string = process . cwd ( ) ;
479+ readonly #isCoordinator: boolean ;
480+ #finalized = false ;
468481
469482 /**
470483 * Create a sharded WAL manager.
484+ *
485+ * @param opt.dir - Base directory to store shard files (defaults to process.cwd())
486+ * @param opt.format - WAL format configuration
487+ * @param opt.groupId - Group ID for sharding (defaults to generated group ID)
488+ * @param opt.coordinatorIdEnvVar - Environment variable name for storing coordinator ID (defaults to CP_SHARDED_WAL_COORDINATOR_ID)
471489 */
472490 constructor ( opt : {
473491 dir ?: string ;
474492 format : Partial < WalFormat < T > > ;
475493 groupId ?: string ;
494+ coordinatorIdEnvVar ?: string ;
476495 } ) {
477- const { dir, format, groupId } = opt ;
496+ const {
497+ dir,
498+ format,
499+ groupId,
500+ coordinatorIdEnvVar = SHARDED_WAL_COORDINATOR_ID_ENV_VAR ,
501+ } = opt ;
478502 this . groupId = groupId ?? getShardedGroupId ( ) ;
479503 if ( dir ) {
480504 this . #dir = dir ;
481505 }
482506 this . #format = parseWalFormat < T > ( format ) ;
507+ this . #isCoordinator = isLeaderWal ( coordinatorIdEnvVar , this . #id) ;
508+ }
509+
510+ /**
511+ * Is this instance the coordinator?
512+ *
513+ * Coordinator status is determined from the coordinatorIdEnvVar environment variable.
514+ * The coordinator handles finalization and cleanup of shard files.
515+ *
516+ * @returns true if this instance is the coordinator, false otherwise
517+ */
518+ isCoordinator ( ) : boolean {
519+ return this . #isCoordinator;
483520 }
484521
485522 shard ( shardId : string = getShardId ( ) ) {
@@ -570,4 +607,14 @@ export class ShardedWal<T extends object | string = object> {
570607 // Directory might not be empty or already removed, ignore
571608 }
572609 }
610+
611+ finalizeIfCoordinator ( ) : void {
612+ if ( this . #finalized) return ;
613+ this . #finalized = true ;
614+
615+ if ( ! this . isCoordinator ( ) ) return ;
616+
617+ this . finalize ( ) ;
618+ this . cleanup ( ) ;
619+ }
573620}
0 commit comments