1 //go:build js && wasm
2 // +build js,wasm
3 4 package idb
5 6 import (
7 "context"
8 "strings"
9 )
10 11 /*
12 RetryTxn retries the function with a new transaction if the txn finishes prematurely.
13 14 IndexedDB transactions automatically commit when all outstanding requests have
15 been satisfied. When a Goroutine is suspended due to a select statement or other
16 context switching, the IndexedDB transation commits automatically, leading to
17 errors with a suffix "The transaction has finished."
18 19 See: https://github.com/w3c/IndexedDB/issues/34 for more details.
20 21 RetryTxn is a mechanism that automatically re-creates the transaction and
22 retries the operation whenever we encounter this specific error. This
23 ensures that operations can continue even if the transaction has been
24 automatically committed.
25 */
26 func RetryTxn(
27 ctx context.Context,
28 db *Database,
29 txnMode TransactionMode,
30 fn func(txn *Transaction) error,
31 objectStoreName string,
32 objectStoreNames ...string,
33 ) error {
34 for {
35 txn, err := db.Transaction(txnMode, objectStoreName, objectStoreNames...)
36 if err != nil {
37 if IsTxnFinishedErr(err) {
38 continue
39 }
40 return err
41 }
42 43 // call the fn
44 err = fn(txn)
45 46 // if the fn returns txn finished, retry.
47 if IsTxnFinishedErr(err) {
48 continue
49 }
50 51 // check for error performing the operation
52 if err != nil {
53 _ = txn.Abort()
54 return err
55 }
56 57 // commit the txn
58 err = txn.Commit()
59 if IsTxnFinishedErr(err) {
60 // txn committed automatically already
61 err = nil
62 }
63 64 return err
65 }
66 }
67 68 // IsTxnFinishedErr checks if an error corresponds to a transaction finishing.
69 // see RetryTxn for details
70 func IsTxnFinishedErr(err error) bool {
71 if err == nil {
72 return false
73 }
74 errStr := err.Error()
75 switch {
76 case strings.HasSuffix(errStr, "The transaction has finished."):
77 return true
78 case strings.HasSuffix(errStr, "The database connection is closing."):
79 return true
80 // Firefox: transaction finished error.
81 case strings.HasSuffix(errStr, "A request was placed against a transaction which is currently not active, or which is finished."):
82 return true
83 default:
84 return false
85 }
86 }
87