@@ -22,6 +22,7 @@ import (
2222 "github.com/hyperledger-labs/fabric-token-sdk/token/services/tokens"
2323 "github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx/dep"
2424 "github.com/hyperledger-labs/fabric-token-sdk/token/services/ttx/finality"
25+ "github.com/hyperledger-labs/fabric-token-sdk/token/services/utils"
2526 token2 "github.com/hyperledger-labs/fabric-token-sdk/token/token"
2627 "go.opentelemetry.io/otel/trace"
2728)
@@ -81,9 +82,11 @@ type Service struct {
8182 metricsProvider metrics.Provider
8283 metrics * Metrics
8384 checkService CheckService
85+ lockConfig * LockConfig
8486}
8587
8688// NewService creates a new auditor Service with the provided dependencies.
89+ // If lockConfig is nil, default lock configuration will be used.
8790func NewService (
8891 tmsID token.TMSID ,
8992 networkProvider NetworkProvider ,
@@ -93,7 +96,12 @@ func NewService(
9396 finalityTracer trace.Tracer ,
9497 metricsProvider metrics.Provider ,
9598 checkService CheckService ,
99+ lockConfig * LockConfig ,
96100) * Service {
101+ if lockConfig == nil {
102+ lockConfig = DefaultLockConfig ()
103+ }
104+
97105 return & Service {
98106 tmsID : tmsID ,
99107 networkProvider : networkProvider ,
@@ -104,6 +112,7 @@ func NewService(
104112 metricsProvider : metricsProvider ,
105113 metrics : newMetrics (metricsProvider ),
106114 checkService : checkService ,
115+ lockConfig : lockConfig ,
107116 }
108117}
109118
@@ -113,7 +122,8 @@ func (a *Service) Validate(ctx context.Context, request *token.Request) error {
113122}
114123
115124// Audit extracts the list of inputs and outputs from the passed transaction.
116- // In addition, Audit acquires locks on the enrollment IDs involved in the transaction.
125+ // In addition, the Audit locks the enrollment named ids with retry logic and exponential backoff
126+ // to prevent livelock conditions.
117127// The caller MUST call Release() to unlock these enrollment IDs after processing.
118128//
119129// IMPORTANT: The defer Release() statement MUST be placed immediately after checking
@@ -141,18 +151,47 @@ func (a *Service) Audit(ctx context.Context, tx Transaction) (*token.InputStream
141151 var eids []string
142152 eids = append (eids , record .Inputs .EnrollmentIDs ()... )
143153 eids = append (eids , record .Outputs .EnrollmentIDs ()... )
144- logger .DebugfContext (ctx , "audit transaction [%s], acquire locks" , tx .ID ())
145- if err := a .auditDB .AcquireLocks (ctx , string (request .Anchor ), eids ... ); err != nil {
154+
155+ // Acquire locks with retry and exponential backoff to prevent livelock
156+ logger .DebugfContext (ctx , "audit transaction [%s], acquire locks with retry" , tx .ID ())
157+ if err := a .acquireLocksWithRetry (ctx , string (request .Anchor ), eids ); err != nil {
146158 a .metrics .AuditLockConflicts .Add (1 )
147159
148160 return nil , nil , err
149161 }
162+
150163 logger .DebugfContext (ctx , "audit transaction [%s], acquire locks done" , tx .ID ())
151164 a .metrics .AuditDuration .Observe (time .Since (start ).Seconds ())
152165
153166 return record .Inputs , record .Outputs , nil
154167}
155168
169+ // acquireLocksWithRetry attempts to acquire locks with exponential backoff and randomized jitter
170+ // to prevent livelock conditions when multiple auditors compete for the same enrollment IDs.
171+ // This implements the mitigation strategy for deadlock/livelock prevention.
172+ func (a * Service ) acquireLocksWithRetry (ctx context.Context , anchor string , eids []string ) error {
173+ // Create a retry runner with jitter support
174+ retryRunner := utils .NewRetryRunnerWithJitter (
175+ logger ,
176+ a .lockConfig .MaxRetries ,
177+ a .lockConfig .InitialBackoff ,
178+ a .lockConfig .MaxBackoff ,
179+ a .lockConfig .BackoffMultiplier ,
180+ a .lockConfig .JitterFactor ,
181+ )
182+
183+ // Use the retry runner to acquire locks
184+ err := retryRunner .RunWithContext (ctx , func () error {
185+ return a .auditDB .AcquireLocks (ctx , anchor , eids ... )
186+ })
187+
188+ if err != nil {
189+ return errors .WithMessagef (err , "failed to acquire locks for anchor [%s]" , anchor )
190+ }
191+
192+ return nil
193+ }
194+
156195// Append adds the passed transaction to the auditor database.
157196// It also releases the locks acquired by Audit.
158197func (a * Service ) Append (ctx context.Context , tx Transaction ) error {
0 commit comments