Pessimistic Offline Lock Implementer
by @quochungto
Use when offline-concurrency-strategy-selector (or your team) has chosen Pessimistic Offline Lock and you need to implement it correctly end-to-end. Handles:...
Example 1: Java/Spring β Insurance Underwriting System
Scenario: Underwriters edit complex insurance policies. Sessions last 45β90 minutes. Policies have Coverages, Endorsements, and Named Insureds. Two underwriters occasionally assigned the same policy. Work loss cost: very high.
Trigger: "Underwriters are furious about 409 errors after 90 minutes of work. Need to lock policies at edit start."
Process:
app_lock(lockable_id BIGINT PK, owner_id VARCHAR, lock_type VARCHAR, acquired_at TIMESTAMP, expires_at TIMESTAMP).EditPolicyCommand acquires lock before policyMapper.find(id). SavePolicyCommand releases after commit. LockRemover registered on HTTP session as HttpSessionBindingListener. Expiry: 120 minutes.version row per Policy; all members reference it). Lock on version.id.find() acquires; update() verifies lock held.Output: DB lock table, 120-minute expiry, LockingMapper, shared version token for Policy aggregate, admin /admin/locks/policy/{id}/release endpoint, LockRemover session listener.
Example 2: Node.js CMS β Article Editing with Redis
Scenario: CMS where editors write and edit published articles. Sessions typically 20β30 minutes. Articles consist of Article + Sections + Tags + Metadata. Node.js, no JVM session management.
Trigger: "Add edit locking to prevent two editors opening the same article simultaneously."
Process:
lock:article:{id}, value: { ownerId, ownerName, acquiredAt }, TTL: 2400s (40 min).GET /articles/:id/edit acquires Redis lock before loading. PUT /articles/:id releases after save. Express middleware registers session-end cleanup via req.session.on('destroy', releaseAllLocksFor(sessionId)). Heartbeat: client pings /session/ping every 30s; absence for 5 minutes triggers server-side expiry.ArticleRepository base class. findForEdit(id) acquires lock; update(article) asserts lock is held.Output: Redis lock with TTL + heartbeat, root lock on Article (aggregate), ArticleRepository.findForEdit() acquires implicitly, session-destroy listener.
Example 3: Python/Django β Order-Picking System
Scenario: Warehouse pickers claim orders from a queue. Once a picker opens an order, it must be locked so two pickers don't pick the same items. Sessions are short (5β15 min, order completion). DB: PostgreSQL.
Trigger: "Two pickers sometimes pick the same order. Add a 'picked by X' locking mechanism with visible status."
Process:
expires_at for 30-minute absolute timeout.POST /orders/{id}/claim acquires lock before loading. POST /orders/{id}/complete releases. Django signal request_started + session middleware for release-on-session-end. Picker's name stored as owner_id (human-readable for UI).OrderRepository.claim_for_picking(order_id, picker_id) is the single entry point; implicit lock built in. No LockingMapper needed (single access path).Output: app_lock table with expires_at, expiry sweep task, Order root lock, claim_for_picking() as single locked access point, UI "PICKED BY" badge, admin lock dashboard.
clawhub install bookforge-pessimistic-offline-lock-implementer