var doInsertReal func(docId string, doc []byte, currentAttempt, maxAttempts int, delay time.Duration) (string, error) doInsertReal = func(docId string, doc []byte, currentAttempt, maxAttempts int, delay time.Duration) (string, error) { _, err := collection.Insert(docId, doc, &gocb.InsertOptions{DurabilityLevel: gocb.DurabilityLevelMajority}) if err != nil { if errors.Is(err, gocb.ErrDocumentExists) { // The logic here is that if we failed to insert on the first attempt then // it's a true error, otherwise we retried due to an ambiguous error, and // it's ok to continue as the operation was actually successful. if currentAttempt == 0 { return "", err } return "ok!", nil // Ambiguous errors. The operation may or may not have succeeded. For inserts, // the insert can be retried, and a DocumentExistsException indicates it was // successful. } else if errors.Is(err, gocb.ErrDurabilityAmbiguous) || errors.Is(err, gocb.ErrTimeout) || // Temporary/transient errors that are likely to be resolved // on a retry. errors.Is(err, gocb.ErrTemporaryFailure) || errors.Is(err, gocb.ErrDurableWriteInProgress) || errors.Is(err, gocb.ErrDurableWriteReCommitInProgress) || // These transient errors won't be returned on an insert, but can be used // when writing similar wrappers for other mutation operations. errors.Is(err, gocb.ErrCasMismatch) { if currentAttempt >= maxAttempts { return "", err } time.Sleep(delay) return doInsertReal(docId, doc, currentAttempt+1, maxAttempts, delay*2) } return "", err } return "ok!", nil }