返回 DeepSeek-Reasonix
store.go
根目录 / internal / taskmonitor / store.go
1 package taskmonitor
2
3 import (
4 "context"
5 "errors"
6 "time"
7 )
8
9 // ErrStoreVersionConflict reports that a snapshot CAS lost to another writer.
10 // Callers may re-read and retry a derived update, or return a stable client
11 // conflict without parsing implementation-specific error text.
12 var ErrStoreVersionConflict = errors.New("task store version conflict")
13
14 // Store is the read-only query surface for task monitoring.
15 type Store interface {
16 ListTasks(ctx context.Context, projectDir string) ([]TaskSnapshot, error)
17 GetTask(ctx context.Context, projectDir string, taskID string) (*TaskSnapshot, error)
18 ListEvents(ctx context.Context, projectDir string, taskID string, afterSequence int) ([]TaskEvent, error)
19 }
20
21 // ProjectionSink receives post-commit hints. Implementations must enqueue and
22 // return immediately; FileStore remains the only authority for task control.
23 type ProjectionSink interface {
24 SnapshotChanged(projectRoot, taskID string)
25 EventsChanged(projectRoot, taskID string)
26 }
27
28 // IdempotencyRecord captures the binding between an idempotency key and the
29 // operation it was used for.
30 type IdempotencyRecord struct {
31 Key string `json:"key"`
32 Op string `json:"op"`
33 TaskID string `json:"task_id"`
34 Version uint64 `json:"version"`
35 Pending bool `json:"pending,omitempty"`
36 ClaimedAt time.Time `json:"claimed_at,omitempty"`
37 }
38
39 // IdempotencyClaimer atomically reserves a key before a control operation
40 // performs any side effect. Pending claims can be finalized or released.
41 type IdempotencyClaimer interface {
42 ClaimIdempotency(ctx context.Context, projectDir string, r IdempotencyRecord) (*IdempotencyRecord, error)
43 FinalizeIdempotency(ctx context.Context, projectDir string, r IdempotencyRecord) error
44 ReleaseIdempotency(ctx context.Context, projectDir, key string) error
45 }
46
47 // WriteStore extends Store with atomic write operations for control
48 // commands, persistent idempotency, and event sequencing.
49 //
50 // Transaction ordering for control operations:
51 // 1. ClaimIdempotency — reserve the key before runtime/state side effects
52 // 2. SaveTask — persist state with version CAS
53 // 3. AppendAuditEvent — atomically assign sequence + write event
54 // 4. FinalizeIdempotency — mark the claim complete
55 //
56 // Steps 2-3 failures after a successful SaveTask leave the task in the new
57 // state with a potentially incomplete audit log. This is acceptable for a
58 // file-based store; a transactional store would provide stronger guarantees.
59 type WriteStore interface {
60 Store
61
62 // SaveTask atomically persists snap with version-based CAS.
63 SaveTask(ctx context.Context, projectDir string, snap TaskSnapshot) error
64
65 // RenewRuntimeLease extends an alive task lease only when ownerID still
66 // owns the persisted runtime generation. Implementations must read the raw
67 // stored snapshot rather than a liveness-reconciled observation.
68 RenewRuntimeLease(ctx context.Context, projectDir, taskID, ownerID string, leaseUntil time.Time) (bool, error)
69
70 // AppendAuditEvent atomically assigns the next monotonic sequence
71 // number and appends the event to taskID's event log. Implementations
72 // must be safe for concurrent use across processes.
73 AppendAuditEvent(ctx context.Context, projectDir string, ev TaskEvent) error
74
75 // CheckIdempotency returns the recorded key if it exists, or nil.
76 CheckIdempotency(ctx context.Context, projectDir string, key string) (*IdempotencyRecord, error)
77
78 // RecordIdempotency atomically claims key for r. If key already exists
79 // with identical parameters, it is a no-op. If key exists with different
80 // parameters, it must return an error. Implementations must be safe
81 // across process restarts.
82 RecordIdempotency(ctx context.Context, projectDir string, r IdempotencyRecord) error
83 }
84
84 lines GO