| 234 | } |
| 235 | |
| 236 | func Example_optimisticLocking() { |
| 237 | // PRAGMA: This example is used on gocloud.dev; PRAGMA comments adjust how it is shown and can be ignored. |
| 238 | // PRAGMA: On gocloud.dev, hide lines until the next blank line. |
| 239 | ctx := context.Background() |
| 240 | |
| 241 | coll, err := memdocstore.OpenCollection("Name", nil) |
| 242 | if err != nil { |
| 243 | log.Fatal(err) |
| 244 | } |
| 245 | defer coll.Close() |
| 246 | |
| 247 | // Create a player. |
| 248 | pat := &Player{Name: "Pat", Score: 7} |
| 249 | if err := coll.Create(ctx, pat); err != nil { |
| 250 | log.Fatal(err) |
| 251 | } |
| 252 | fmt.Println(pat) // memdocstore revisions are deterministic, so we can check the output. |
| 253 | |
| 254 | // Double a player's score. We cannot use Update to multiply, so we use optimistic |
| 255 | // locking instead. |
| 256 | |
| 257 | // We may have to retry a few times; put a time limit on that. |
| 258 | ctx, cancel := context.WithTimeout(ctx, 30*time.Second) |
| 259 | defer cancel() |
| 260 | for { |
| 261 | // Get the document. |
| 262 | player := &Player{Name: "Pat"} |
| 263 | if err := coll.Get(ctx, player); err != nil { |
| 264 | log.Fatal(err) |
| 265 | } |
| 266 | // player.DocstoreRevision is set to the document's revision. |
| 267 | |
| 268 | // Modify the document locally. |
| 269 | player.Score *= 2 |
| 270 | |
| 271 | // Replace the document. player.DocstoreRevision will be checked against |
| 272 | // the stored document's revision. |
| 273 | err := coll.Replace(ctx, player) |
| 274 | if err != nil { |
| 275 | code := gcerrors.Code(err) |
| 276 | // On FailedPrecondition or NotFound, try again. |
| 277 | if code == gcerrors.FailedPrecondition || code == gcerrors.NotFound { |
| 278 | continue |
| 279 | } |
| 280 | log.Fatal(err) |
| 281 | } |
| 282 | fmt.Println(player) |
| 283 | break |
| 284 | } |
| 285 | |
| 286 | // Output: |
| 287 | // &{Pat 7 1} |
| 288 | // &{Pat 14 2} |
| 289 | } |