mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-03 13:12:09 +00:00
refactor(shared-manager): tighten canonical identity capture
Build the canonical identity from the stat getCanonicalFile already takes, instead of a second lstat of the same inode. One syscall less, and mode, mtime and identity now describe the same moment rather than two adjacent ones. Assert in the adoption race tests that the foreign writer never fired. It writes only when the canonical path is observed empty, so a zero count states the invariant the fix establishes - the path is never left without a regular file - instead of only checking the final content. Built [OnSteroids](https://onsteroids.ai)
This commit is contained in:
1 parent
00a4dceb94
commit
fa3fd8eb3a
2 files changed
+10
-10
No files matched your search
@@ -148,9 +148,7 @@ interface CanonicalIdentity {
|
||||
size: number;
|
||||
}
|
||||
|
||||
function readCanonicalIdentity(writePath: string): CanonicalIdentity | null {
|
||||
const stats = getLstatSync(writePath);
|
||||
if (!stats?.isFile()) return null;
|
||||
function canonicalIdentityOf(stats: fs.Stats): CanonicalIdentity {
|
||||
return { ino: stats.ino, mtimeMs: stats.mtimeMs, size: stats.size };
|
||||
}
|
||||
|
||||
@@ -246,9 +244,7 @@ function publishCanonicalContent(
|
||||
descriptor = null;
|
||||
|
||||
const currentStats = getLstatSync(writePath);
|
||||
const current = currentStats?.isFile()
|
||||
? { ino: currentStats.ino, mtimeMs: currentStats.mtimeMs, size: currentStats.size }
|
||||
: null;
|
||||
const current = currentStats?.isFile() ? canonicalIdentityOf(currentStats) : null;
|
||||
if (!canonicalIdentityMatches(expected, current)) {
|
||||
throw Object.assign(new TypeError(`Canonical file changed during adoption: ${writePath}`), {
|
||||
code: 'EEXIST',
|
||||
@@ -297,12 +293,12 @@ function getCanonicalFile(canonicalPath: string): {
|
||||
});
|
||||
}
|
||||
|
||||
// Identity first: a write landing between the two reads leaves us holding
|
||||
// newer bytes than the identity describes, and publication fails closed.
|
||||
const identity = readCanonicalIdentity(writePath);
|
||||
// The identity describes the inode as of the stat above, taken before the
|
||||
// content read: a write landing in between leaves us holding newer bytes
|
||||
// than the identity describes, and publication fails closed.
|
||||
return {
|
||||
content: fs.readFileSync(writePath),
|
||||
identity,
|
||||
identity: canonicalIdentityOf(canonicalStats),
|
||||
mode: canonicalStats.mode & 0o777,
|
||||
mtimeMs: canonicalStats.mtimeMs,
|
||||
writePath,
|
||||
|
||||
@@ -686,6 +686,10 @@ describe('SharedManager', () => {
|
||||
foreignWriter.restore();
|
||||
}
|
||||
|
||||
// The writer only fires when the canonical path is observed empty, so
|
||||
// zero writes is the invariant itself: adoption never left the path
|
||||
// without a regular file.
|
||||
expect(foreignWriter.placeholderWrites()).toBe(0);
|
||||
expect(fs.existsSync(canonicalPath)).toBe(true);
|
||||
expect(fs.readFileSync(canonicalPath, 'utf8')).not.toBe(placeholder);
|
||||
expect([divergedSettings, previousSettings]).toContainEqual(readJson(canonicalPath));
|
||||
|
||||
Reference in new issue
Block a user