feat(dav): serialize concurrent MOVE/COPY - #62727
Conversation
I think we can do this even more cleanly by implementing actual operation-scoped locking below DAV so UI/jobs/other Node/View callers can participate too. It'll provide broader coverage outside of just DAV and not have similar limitations. Technically we already have the capability today using our existing locking primitives and w/o introducing a new locking layer (and use it in the encryption app in a small way), but I want to wrap it anyhow to make a cleaner API (internal for now) as a formal operational locking service. I actually got about 80% of the way through a proof-of-concept when I saw your Issue pop up the other day (implementing the internal API + adjusting |
|
You're right. Thanks for raising this! The DAV plugin in this PR rejects at request entry via |
Assisted-by: ClaudeCode:claude-opus-4-7 Signed-off-by: Tobias Harnickell <tobias.harnickell@bedag.ch>
fb6d331 to
78e3736
Compare
Summary
Serialize concurrent WebDAV MOVE and COPY on the same source or destination path via a new
SerializeMoveCopyPluginregistered at the DAV entry. When enabled, a conflicting concurrent operation MUST receive HTTP 423 Locked. Concurrent COPY from one source to different destinations remains allowed.Lock scheme
LOCK_EXCLUSIVELOCK_EXCLUSIVELOCK_SHAREDLOCK_EXCLUSIVELocks share the namespace
webdav-serialize:<davPath>. Path-sorted acquisition prevents deadlock between two concurrent operations with swapped source and destination. Source == destination short-circuits with no lock (defensive).Config
Set
dav.serialize_move_copytotrueinconfig/config.sample.php. Defaults tofalse.Behaviour matrix (op B arrives while A in flight)
Cross-user contention on the same shared file is serialized one layer down. The md5 lock key at
Common.php:696collapses both users onto one key (owner-storage id plus owner-internal path).ObjectTree::copywraps the storage-layer\OCP\Lock\LockedExceptionto HTTP 423 for COPY.Node::setNamedoes not wrap for MOVE and surfaces HTTP 500..Known limitations
ILockingProviderprovides no fair-lock primitive.filelocking.ttldefault 3600 s exceeds typicalmax_execution_time. The lock outlives the request only on runaway workloads. Same posture as inner-lock behaviour.TODO
.devcontainerstackChecklist
apps/dav/tests/unit/Connector/Sabre/SerializeMoveCopyPluginTest.php. Unit suite: 11 tests, 48 assertions, all passing on PHP 8.4.23 + PHPUnit 11.5.50. E2E matrix (10 rows,.devcontainerstack, 5 MB source, 100 ms stagger): all rows PASS. Contended rows produce one 20x and one 423. Allowed rows (fan-out COPY) produce two 20x.3. to review, feature component)stable32)AI (if applicable)