Skip to content

fix(storage): filter undeletable staged keys - #774

Open
Voyagerroc-Lab wants to merge 1 commit into
Basekick-Labs:mainfrom
Voyagerroc-Lab:fix/filter-undeletable-staged-keys
Open

Voyagerroc-Lab wants to merge 1 commit into
Basekick-Labs:mainfrom
Voyagerroc-Lab:fix/filter-undeletable-staged-keys

Conversation

@Voyagerroc-Lab

Copy link
Copy Markdown

Summary\n\nCloses #772. ListStaged now validates each owning key with the same rule used by DeleteStaged, so reclamation never receives a path it cannot address. Adds a regression test for an overlong staged key and updates the planned release notes.\n\n## Validation\n\ngo test ./internal/storage/... and go vet could not run because Go is not installed in this Windows environment.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the contribution!

Before this can be merged, please sign the Contributor License Agreement.
Arc is AGPL-3.0 and Basekick Labs also ships commercially licensed builds; the CLA is what lets your contribution be included in both. You keep full ownership of your work.

To sign, post a new comment on this pull request containing the
sentence below. Copy the sentence only: the dashed lines around it
are formatting, and including them stops the signature from
registering.


I have read the CLA Document and I hereby sign the CLA


Voyagerroc-Code seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@Voyagerroc-Lab
Voyagerroc-Lab force-pushed the fix/filter-undeletable-staged-keys branch from e5a4528 to 8bdcc5b Compare September 13, 2026 16:59

@xe-nvdk xe-nvdk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for picking up #772. I built and ran this branch, and it cannot go in as it stands. The first two items are why we ask contributors to run the tests before opening a PR: everything else in a review is guesswork until the code compiles.

1. The test does not compile (blocking). internal/storage/staging_test.go:212:

assignment mismatch: 2 variables but b.DeleteStaged returns 1 value

DeleteStaged returns only an error. Because the file is in the package's test build, this fails every test in internal/storage, not just the new one. I approved the CI run so you can see it on the PR. The description says Go was not installed in your environment; please install it (go.dev/dl, Windows builds are fine) and run go build ./... && go vet ./internal/storage/ && go test ./internal/storage/... before pushing again. We do not review PRs that have not been built.

2. The fixture cannot exist (blocking). With the compile error patched, the test fails at os.WriteFile:

open .../db/kkkk…kkkk.part: file name too long

MaxUsableKeySegmentLen+1 is 251 bytes, plus .part is 256, over the 255-byte filename limit on every POSIX filesystem. A test for "a key the contract refuses but the disk accepts" has to use a key of that shape. A backslash does it: with key := "db/bad\\key" written at key+PartSuffix, I verified on your branch that ListStaged("db/") returns nothing, DeleteStaged(key) refuses it, and ListUnusable("db/") still reports the file.

3. The change introduces a panic (blocking). validateKeyBody indexes p[0] and assumes a non-empty key; both existing callers (ValidateKey, stagedPath) check key == "" and the trailing / before calling it. Your ListStaged calls it directly. A .part file at the listing root gives rel == ".part", so key == "", and on this branch:

ListStaged(ctx, "") with a root-level ".part" file → runtime error: index out of range [0] with length 0

Before this PR that entry was reported and then refused by DeleteStaged with an error; now the walk panics, and the shutdown sweep lists with an empty prefix. The fix is also what makes the PR description true: stagedPath applies three checks, and "the same rule used by DeleteStaged" means all three. Extract them into one helper and call it from both places:

// validateStagedKey is the rule DeleteStaged applies to an owning key.
// ListStaged uses the same function so it never reports a key DeleteStaged refuses.
func validateStagedKey(key string) error {
	if key == "" {
		return fmt.Errorf("%w: key is empty", ErrInvalidPath)
	}
	if key[len(key)-1] == '/' {
		return fmt.Errorf("%w: %q ends in a separator", ErrInvalidPath, key)
	}
	return validateKeyBody(key)
}

stagedPath becomes if err := validateStagedKey(key); err != nil { return "", err } followed by return partPath(...), and ListStaged skips an entry when the helper errors. Two copies of the rule is how these drift apart; one function is the point.

4. What #772 asked for. Three things from the issue are missing: the invariant test ("every key ListStaged returns is accepted by DeleteStaged", a table with accepted and refused keys, not one negative case); an assertion that the filtered file is still reported by ListUnusable; and a line in the PR description acknowledging that the committed-.part ambiguity is out of scope. Add a root-level .part case to the table so item 3 has a regression test.

5. PR hygiene.

  • The description is one line with literal \n in it. Please write it as markdown, with the test plan listing what you actually ran and saw.
  • The commit is authored as 325343927+Voyagerroc-Code@users.noreply.github.com. That account does not exist, so GitHub cannot attribute the commit and the CLA bot cannot match a signature from @Voyagerroc-Lab. Re-author with the noreply address of the account you sign from (git commit --amend --reset-author after setting user.email), force-push, sign, then comment recheck.
  • Rebase on main (two commits behind, no conflict).

Release-note placement and the credit line are right. Add the issue link to the heading like its neighbours, ([#772](https://github.com/Basekick-Labs/arc/issues/772)), and say what an operator stops seeing (the per-run "Failed to reclaim staged partial" warning on DROP DATABASE).

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