Skip to content

fix(selectivity): ensure selectivity dump has no stale data#1214

Merged
KuznetsovRoman merged 2 commits intomasterfrom
TESTPLANE-921.clear_dump_unused
Mar 18, 2026
Merged

fix(selectivity): ensure selectivity dump has no stale data#1214
KuznetsovRoman merged 2 commits intomasterfrom
TESTPLANE-921.clear_dump_unused

Conversation

@KuznetsovRoman
Copy link
Member

No description provided.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 27, 2026

Open in StackBlitz

npm i https://pkg.pr.new/gemini-testing/testplane@1214

commit: af5b616

@KuznetsovRoman KuznetsovRoman force-pushed the TESTPLANE-921.clear_dump_unused branch 2 times, most recently from 2527862 to 19810a9 Compare February 27, 2026 00:56
dependencies.modules.forEach(dependency => this._addModuleDependency(dependency));
}

async commit(): Promise<void> {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing logic to "not remove anything extra"
It was useful when hash writes were concurrent

}

// File was not used in this run
if (fileStat.atimeMs < performance.timeOrigin && fileStat.isFile()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"atime" - time file was accessed
"clearUnusedSelectivityDumps" is called after successful run
If file was not accessed in successful run, we can safely delete it

try {
const stats = await libUVLimited(() => fs.stat(flagFilePath));
return stats.mtimeMs >= processStartTime;
return stats.mtimeMs >= performance.timeOrigin;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out, there is special value for that, so we dont need "new Date()" hacks

@KuznetsovRoman
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 19810a9bc8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +95 to +99
await writeTo(this._stagedFileHashes, fileContents.files);
await writeTo(this._stagedModuleHashes, fileContents.modules);
await writeTo(this._stagedPatternHashes, fileContents.patterns);

markAsCommited(updatedModules, this._stagedModuleHashes);
markAsCommited(updatedFiles, this._stagedFileHashes);
await writeJsonWithCompression(this._selectivityHashesPath, fileContents, this._compresion);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Merge existing hashes before overwriting hashes.json

HashWriter.save now writes a brand-new hash object populated only from currently staged entries, but staging is filled only from executed tests (onTestDependencies) and changed disable patterns (updateSelectivityHashes). This drops hashes for skipped tests and unchanged patterns, so on the next process run HashReader.getTestChangedDeps/patternHasChanged read undefined and treat those dependencies as changed, causing selectivity to rerun tests (or whole browsers) even when source files are unchanged.

Useful? React with 👍 / 👎.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now writes a brand-new hash object populated only from currently staged entries

Yes.

but staging is filled only from executed tests

No. It is also filled in master thread while calculating current file hashes.

and changed disable patterns

Unchanged too. For same reasons.

}

// File was not used in this run
if (fileStat.atimeMs < performance.timeOrigin && fileStat.isFile()) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop using atime as a per-run usage marker

The stale-file check deletes dumps based on fileStat.atimeMs < performance.timeOrigin, but on filesystems mounted with relatime/noatime a read during this run may not update atime, so actively used dependency dumps can still look stale and get removed. In those environments, the cleanup step can delete valid selectivity dumps and force unnecessary full reruns on the next execution.

Useful? React with 👍 / 👎.


if (accessError) {
if (!("code" in accessError && accessError.code === "ENOENT")) {
debugSelectivity(`Couldn't access "${testsPath}" to clear stale files: %O`, accessError);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not actionable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are debug logs, are they supposed to be actionable to user?
I thought, they need to be clear to us


for (const browserId of browserIds) {
const browserConfig = config.forBrowser(browserId);
const { enabled, testDependenciesPath } = browserConfig.selectivity;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but if I use grep option, selectivity will be disabled, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"clearUnusedSelectivityDumps" is called in "testplane.ts" inside:

if (!shouldDisableSelectivity && !this.isFailed())

branch, with "updateSelectivityHashes" function call.
Both will not be called because "shouldDisableSelectivity" will be "true" in this case (because of "grep")

filesDeleted++;
})
.catch(err => {
debugSelectivity(`Couldn't remove stale file "${filePath}": %O`, err);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not actionable

Copy link
Member Author

@KuznetsovRoman KuznetsovRoman Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there are plenty of code like this, i will address the issue "actionable fs-errors" in the following pr to avoid code duplication + merge issues

assert.calledOnce(getSelectivityTestsPathStub);
assert.calledWith(getSelectivityTestsPathStub, "/test/deps");
assert.calledOnce(fsStub.access);
assert.calledWith(fsStub.access, "/test/deps/tests", 6); // R_OK | W_OK = 4 | 2 = 6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

R_OK + W_OK = 4 + 2 = 6?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite.
0b00000100
0b00000010
0b00000110
Its bitwise "or", not "sum"

@KuznetsovRoman KuznetsovRoman force-pushed the TESTPLANE-921.clear_dump_unused branch from 19810a9 to 93c7d77 Compare March 18, 2026 14:41
@KuznetsovRoman KuznetsovRoman force-pushed the TESTPLANE-921.clear_dump_unused branch from 93c7d77 to af5b616 Compare March 18, 2026 14:46
@KuznetsovRoman KuznetsovRoman merged commit 0bc46f6 into master Mar 18, 2026
5 of 7 checks passed
@KuznetsovRoman KuznetsovRoman deleted the TESTPLANE-921.clear_dump_unused branch March 18, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants