| 470 | } |
| 471 | |
| 472 | func ExampleClient_Watch() { |
| 473 | const maxRetries = 10000 |
| 474 | |
| 475 | // Increment transactionally increments key using GET and SET commands. |
| 476 | increment := func(key string) error { |
| 477 | // Transactional function. |
| 478 | txf := func(tx *redis.Tx) error { |
| 479 | // Get current value or zero. |
| 480 | n, err := tx.Get(ctx, key).Int() |
| 481 | if err != nil && err != redis.Nil { |
| 482 | return err |
| 483 | } |
| 484 | |
| 485 | // Actual operation (local in optimistic lock). |
| 486 | n++ |
| 487 | |
| 488 | // Operation is committed only if the watched keys remain unchanged. |
| 489 | _, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error { |
| 490 | pipe.Set(ctx, key, n, 0) |
| 491 | return nil |
| 492 | }) |
| 493 | return err |
| 494 | } |
| 495 | |
| 496 | for i := 0; i < maxRetries; i++ { |
| 497 | err := rdb.Watch(ctx, txf, key) |
| 498 | if err == nil { |
| 499 | // Success. |
| 500 | return nil |
| 501 | } |
| 502 | if err == redis.TxFailedErr { |
| 503 | // Optimistic lock lost. Retry. |
| 504 | continue |
| 505 | } |
| 506 | // Return any other error. |
| 507 | return err |
| 508 | } |
| 509 | |
| 510 | return errors.New("increment reached maximum number of retries") |
| 511 | } |
| 512 | |
| 513 | var wg sync.WaitGroup |
| 514 | for i := 0; i < 100; i++ { |
| 515 | wg.Add(1) |
| 516 | go func() { |
| 517 | defer wg.Done() |
| 518 | |
| 519 | if err := increment("counter3"); err != nil { |
| 520 | fmt.Println("increment error:", err) |
| 521 | } |
| 522 | }() |
| 523 | } |
| 524 | wg.Wait() |
| 525 | |
| 526 | n, err := rdb.Get(ctx, "counter3").Int() |
| 527 | fmt.Println("ended with", n, err) |
| 528 | // Output: ended with 100 <nil> |
| 529 | } |