Skip to content

Fix/propagator close errors - #848

Open
butonic wants to merge 2 commits into
opencloud-eu:mainfrom
butonic:fix/propagator-close-errors
Open

butonic wants to merge 2 commits into
opencloud-eu:mainfrom
butonic:fix/propagator-close-errors

Conversation

@butonic

@butonic butonic commented Oct 5, 2026

Copy link
Copy Markdown
Member

Four places in the two copies of decomposedfs throw away the error returned when
releasing a parent node's lock during treetime/treesize propagation. This makes
them observable.

Why

Propagation opens the parent's lock file, and both copies have code that tries
to report a failing close/release — but the reporting is inert:

pkg/storage/utils/decomposedfs (legacy copy: ocis driver, datagateway/ocs)

  • tree/propagator/sync.go — propagateItem had unnamed results, so the deferred
    err = cerr assigned to the local err declared by f, err := lockedfile.OpenFile(...)
    after the return values had already been fixed. The error never reached the caller,
    so the comment ("always log error if closing node fails") and the if err == nil
    guard were aspirational. Naming the results makes the close error reach Propagate,
    which logs it and returns it. (f, err := had to become f, err = because named
    results live in the function body scope, leaving no new variable for :=.)
  • tree/propagator/async.go — the same assignment sits in propagate, which returns
    nothing, so it could never be observed. Replaced with the log the sibling early-release
    path already emits (Failed to close node and release lock).

pkg/storage/pkg/decomposedfs (maintained fork: posix, decomposed, decomposeds3)

  • tree/propagator/sync.go and tree/propagator/async.go — defer func() { _ = unlock() }()
    discards the error from closing the .mlock file (xattrs / messagepack / hybrid backends).
  • tree/propagator/async.go early release — _ = unlock() likewise discarded it. The
    os.ErrClosed filter is load-bearing here: the async propagator releases the lock early,
    so the deferred unlock always runs a second time and sees ErrClosed on the happy path.

Behaviour change

  • utils copy, sync propagation: a failed lock-file close now aborts the parent walk and
    returns an error from Propagate, where previously the walk continued silently. Expect more
    surfaced errors in the operations that trigger propagation; the underlying cause (I/O /
    metadata failure) was already there, just invisible.
  • everything else is log-only — no control flow change.

Why the pkg fork logs instead of returning

The lock file is created empty and never written, so there are no dirty pages to lose — a
failing close signals an I/O error on the handle, not lost data. Aborting propagation (and
failing the upload/copy that triggered it) would be out of proportion, so the error is logged.
The utils copy keeps returning it because that code path was already written to do so.

propagateItem's deferred f.Close() assigned to a local err that was
never part of the return values, so a failed close of the parent's lock
file went unnoticed while the walk carried on and the parent was left
with a possibly stale treesize/tmtime. Naming the results makes the
close error reach Propagate, which logs it and aborts the walk.

The async propagator had the same assignment inside a void function,
where it could never be observed. Log the close error there instead,
which is what the surrounding comment promises.
The posix and decomposed drivers use the pkg fork of decomposedfs, whose
propagators threw away the error from unlock(), which closes the node's
.lock file (xattrs, messagepack and hybrid backends). A failing close on
NFS was therefore invisible.

Log it instead of returning it: the lock file is created empty and never
written, so no data is at stake, and changing propagation semantics is
out of proportion to what is lost. Already-closed errors are filtered
out because the async propagator releases the lock early and the
deferred unlock then runs a second time.
@butonic
butonic requested a review from aduffeck October 5, 2026 15:22
@butonic butonic self-assigned this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Qualification

Development

Successfully merging this pull request may close these issues.

1 participant