| 362 | } |
| 363 | |
| 364 | func (c *counterInt64) TryUpdate(ctx context.Context, newValue int64) int64 { |
| 365 | ctx, span := common.StartSpan(ctx, "CounterInt64.TryUpdate", |
| 366 | trace.WithAttributes( |
| 367 | attribute.String("key", c.key), |
| 368 | ), |
| 369 | ) |
| 370 | defer span.End() |
| 371 | if common.IsTracingDetailed { |
| 372 | span.SetAttributes( |
| 373 | attribute.Int64("new_value", newValue), |
| 374 | ) |
| 375 | } |
| 376 | // IMPORTANT: TryUpdate must never wait on updateMu. |
| 377 | // updateMu exists to coordinate expensive refresh execution in TryUpdateIfStale (thundering herd control), |
| 378 | // but local counter advancement must remain fast even if a refresh is in-flight. |
| 379 | updated := c.processNewValue(UpdateSourceTryUpdate, newValue) |
| 380 | |
| 381 | // Schedule background push when value was actually updated (increase OR decrease). |
| 382 | // With unified semantics, all value changes are propagated to remote. |
| 383 | // Note: Value can be 0 for valid cases like earliest block = genesis. |
| 384 | if updated { |
| 385 | c.scheduleBackgroundPushCurrent() |
| 386 | } |
| 387 | return c.value.Load() |
| 388 | } |
| 389 | |
| 390 | func (c *counterInt64) TryUpdateIfStale(ctx context.Context, staleness time.Duration, executeNewValueFn func(ctx context.Context) (int64, error)) (int64, error) { |
| 391 | ctx, span := common.StartSpan(ctx, "CounterInt64.TryUpdateIfStale", |