Interface CurationLockService
- All Known Implementing Classes:
CurationLockServiceImpl
🛑 Advisory. The correctness guarantee for concurrent curation
writes is the optimistic-concurrency token — the curation commit checks
baseline.lastModified and returns 409 when the dataset moved. This
lock exists so that 409 rarely fires and so a curator can see who else is
working. Nothing here may become the thing that makes concurrent writes
safe.
🛑 No audit events. Any audit event sets
curationDetails.lastUpdated, which is that same token, so taking a
lock would 409 every draft in flight on the dataset. The row carries
stolenFrom / stolenAt and is its own record.
As with the rest of the curation surface, the holder identity is passed in rather than read from the security context: curation reaches Gemma through the agent, so the principal is normally not the person holding the lock.
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic classThrown when a lock is held by someone else and the caller did not ask to steal it. -
Field Summary
Fields -
Method Summary
Modifier and TypeMethodDescriptionacquire(Investigation ee, String lockedBy, boolean steal, int ttlMinutes) Take or refresh the lock.acquire(Investigation ee, String lockedBy, boolean steal, int ttlMinutes, String runId, String agentName) Asacquire(Investigation, String, boolean, int), recording WHAT is taking the lock as well as who it is for.current(Collection<Long> investigationIds) The current holders of many datasets, keyed by dataset id, in ONE query.current(Investigation ee) The current holder, or empty when the dataset is free.booleanRelease whoever holds it.booleanisHeldBy(Investigation ee, String username) Whetherusernameholds an unexpired lock.refresh(Investigation ee, String heldBy, int ttlMinutes) Extend the caller's existing lease without taking one they do not hold.booleanrelease(Investigation ee, String heldBy) Release the lock ifheldByholds it.
-
Field Details
-
DEFAULT_TTL_MINUTES
static final int DEFAULT_TTL_MINUTESDefault lease length. Refreshed by curator activity.- See Also:
-
-
Method Details
-
acquire
Take or refresh the lock.- Parameters:
steal- permit taking a lock another curator currently holds. Always available to callers by design — there is no unlock ceremony to forget, and a steal destroys nothing, since the displaced curator's DRAFT is a separate row.- Returns:
- the granted lock
- Throws:
CurationLockService.CurationLockedException- if the lock is held by someone else andstealis false
-
acquire
CurationLock acquire(Investigation ee, String lockedBy, boolean steal, int ttlMinutes, @Nullable String runId, @Nullable String agentName) Asacquire(Investigation, String, boolean, int), recording WHAT is taking the lock as well as who it is for.runId/agentNameare null for a person, which is how a reader tells a curator from a job. SeeCurationLock.getRunId()for why this is stored rather than derived. -
refresh
Extend the caller's existing lease without taking one they do not hold. Called on every draft autosave, so working holds the lock and walking away releases it.- Returns:
- the refreshed lock, or empty if
heldBydoes not hold it — a refresh must never quietly become an acquire, or an autosave would steal a lock the curator never asked for
-
release
Release the lock ifheldByholds it.- Returns:
- whether a lock was released
-
forceRelease
Release whoever holds it. For administrators. -
current
The current holder, or empty when the dataset is free.An expired row reads as empty rather than as a holder: nothing sweeps expiry, so "is it locked" has to mean "is there an unexpired claim".
-
current
The current holders of many datasets, keyed by dataset id, in ONE query.Exists because the question is asked about a LIST: the curation queue pages up to 1000 rows, and asking per row is 1000 round-trips to paint one screen. A dataset nobody holds is simply absent from the map rather than mapped to null.
Same expiry rule as
current(Investigation)— a lapsed claim is not a holder — so a caller can treat presence in this map as "held right now" without re-checking dates. -
isHeldBy
Whetherusernameholds an unexpired lock. This is what sign-off checks.
-