feat(migration): add lock heartbeats, predictive dry-run planning, and stricter ledger option validation
This commit is contained in:
@@ -22,21 +22,24 @@ export class S3Ledger extends Ledger {
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
const exists = await (this.bucket as any).fastExists({ path: this.path });
|
||||
if (!exists) {
|
||||
await this.write(emptyLedgerData());
|
||||
}
|
||||
// Intentionally empty. The sidecar object is created lazily on first write
|
||||
// so `plan()` / `dryRun` stay read-only for S3-backed ledgers.
|
||||
}
|
||||
|
||||
public async read(): Promise<ISmartMigrationLedgerData> {
|
||||
const buffer = await (this.bucket as any).fastGet({ path: this.path });
|
||||
const exists = await this.bucket.fastExists({ path: this.path });
|
||||
if (!exists) {
|
||||
return emptyLedgerData();
|
||||
}
|
||||
|
||||
const buffer = await this.bucket.fastGet({ path: this.path });
|
||||
const data = JSON.parse(buffer.toString('utf8')) as Partial<ISmartMigrationLedgerData>;
|
||||
return this.normalize(data);
|
||||
}
|
||||
|
||||
public async write(data: ISmartMigrationLedgerData): Promise<void> {
|
||||
const json = JSON.stringify(data, null, 2);
|
||||
await (this.bucket as any).fastPut({
|
||||
await this.bucket.fastPut({
|
||||
path: this.path,
|
||||
contents: json,
|
||||
overwrite: true,
|
||||
@@ -67,6 +70,24 @@ export class S3Ledger extends Ledger {
|
||||
return verify.lock.holder === holderId;
|
||||
}
|
||||
|
||||
public async renewLock(holderId: string, ttlMs: number): Promise<boolean> {
|
||||
const data = await this.read();
|
||||
if (data.lock.holder !== holderId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
data.lock = {
|
||||
holder: holderId,
|
||||
acquiredAt: data.lock.acquiredAt ?? now.toISOString(),
|
||||
expiresAt: new Date(now.getTime() + ttlMs).toISOString(),
|
||||
};
|
||||
await this.write(data);
|
||||
|
||||
const verify = await this.read();
|
||||
return verify.lock.holder === holderId;
|
||||
}
|
||||
|
||||
public async releaseLock(holderId: string): Promise<void> {
|
||||
const data = await this.read();
|
||||
if (data.lock.holder !== holderId) {
|
||||
|
||||
Reference in New Issue
Block a user