Skip to content

Commit 0f301c7

Browse files
Retry on firestore index create 409 with 'cross-transaction contention' (#9515) (#16618)
[upstream:0126c3d731f79d8e38905271a8430b3d1676544f] Signed-off-by: Modular Magician <[email protected]>
1 parent 37d155c commit 0f301c7

File tree

5 files changed

+52
-24
lines changed

5 files changed

+52
-24
lines changed

.changelog/9515.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
firestore: retried resource creation for error 409 with the text "Aborted due to cross-transaction contention" in `google_firestore_index `
3+
```

google/services/firestore/resource_firestore_index.go

+22-19
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,14 @@ func resourceFirestoreIndexCreate(d *schema.ResourceData, meta interface{}) erro
238238
}
239239

240240
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
241-
Config: config,
242-
Method: "POST",
243-
Project: billingProject,
244-
RawURL: url,
245-
UserAgent: userAgent,
246-
Body: obj,
247-
Timeout: d.Timeout(schema.TimeoutCreate),
241+
Config: config,
242+
Method: "POST",
243+
Project: billingProject,
244+
RawURL: url,
245+
UserAgent: userAgent,
246+
Body: obj,
247+
Timeout: d.Timeout(schema.TimeoutCreate),
248+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.FirestoreIndex409CrossTransactionContetion},
248249
})
249250
if err != nil {
250251
return fmt.Errorf("Error creating Index: %s", err)
@@ -322,11 +323,12 @@ func resourceFirestoreIndexRead(d *schema.ResourceData, meta interface{}) error
322323
}
323324

324325
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
325-
Config: config,
326-
Method: "GET",
327-
Project: billingProject,
328-
RawURL: url,
329-
UserAgent: userAgent,
326+
Config: config,
327+
Method: "GET",
328+
Project: billingProject,
329+
RawURL: url,
330+
UserAgent: userAgent,
331+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.FirestoreIndex409CrossTransactionContetion},
330332
})
331333
if err != nil {
332334
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("FirestoreIndex %q", d.Id()))
@@ -381,13 +383,14 @@ func resourceFirestoreIndexDelete(d *schema.ResourceData, meta interface{}) erro
381383
}
382384

383385
res, err := transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
384-
Config: config,
385-
Method: "DELETE",
386-
Project: billingProject,
387-
RawURL: url,
388-
UserAgent: userAgent,
389-
Body: obj,
390-
Timeout: d.Timeout(schema.TimeoutDelete),
386+
Config: config,
387+
Method: "DELETE",
388+
Project: billingProject,
389+
RawURL: url,
390+
UserAgent: userAgent,
391+
Body: obj,
392+
Timeout: d.Timeout(schema.TimeoutDelete),
393+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.FirestoreIndex409CrossTransactionContetion},
391394
})
392395
if err != nil {
393396
return transport_tpg.HandleNotFoundError(err, d, "Index")

google/services/firestore/resource_firestore_index_generated_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,12 @@ func testAccCheckFirestoreIndexDestroyProducer(t *testing.T) func(s *terraform.S
152152
}
153153

154154
_, err = transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
155-
Config: config,
156-
Method: "GET",
157-
Project: billingProject,
158-
RawURL: url,
159-
UserAgent: config.UserAgent,
155+
Config: config,
156+
Method: "GET",
157+
Project: billingProject,
158+
RawURL: url,
159+
UserAgent: config.UserAgent,
160+
ErrorRetryPredicates: []transport_tpg.RetryErrorPredicateFunc{transport_tpg.FirestoreIndex409CrossTransactionContetion},
160161
})
161162
if err == nil {
162163
return fmt.Errorf("FirestoreIndex still exists at %s", url)

google/transport/error_retry_predicates.go

+10
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,16 @@ func FirestoreField409RetryUnderlyingDataChanged(err error) (bool, string) {
332332
return false, ""
333333
}
334334

335+
// relevant for firestore in datastore mode
336+
func FirestoreIndex409CrossTransactionContetion(err error) (bool, string) {
337+
if gerr, ok := err.(*googleapi.Error); ok {
338+
if gerr.Code == 409 && strings.Contains(gerr.Body, "Aborted due to cross-transaction contention") {
339+
return true, "aborted due to cross-transaction contention - retrying"
340+
}
341+
}
342+
return false, ""
343+
}
344+
335345
func IapClient409Operation(err error) (bool, string) {
336346
if gerr, ok := err.(*googleapi.Error); ok {
337347
if gerr.Code == 409 && strings.Contains(strings.ToLower(gerr.Body), "operation was aborted") {

google/transport/error_retry_predicates_test.go

+11
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,14 @@ func TestFirestoreField409_retryUnderlyingDataChanged(t *testing.T) {
183183
t.Errorf("Error not detected as retryable")
184184
}
185185
}
186+
187+
func TestFirestoreIndex409_crossTransactionContetion(t *testing.T) {
188+
err := googleapi.Error{
189+
Code: 409,
190+
Body: "Aborted due to cross-transaction contention",
191+
}
192+
isRetryable, _ := FirestoreIndex409CrossTransactionContetion(&err)
193+
if !isRetryable {
194+
t.Errorf("Error not detected as retryable")
195+
}
196+
}

0 commit comments

Comments
 (0)