From a976c16c598155ceac3b6bd1c7eb85cb2aac697a Mon Sep 17 00:00:00 2001 From: Daniel Cadenas Date: Tue, 15 Oct 2024 20:08:30 -0300 Subject: [PATCH] Don't mix reads and writes in firestore --- .../adapters/firestore/repository_event.go | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/service/adapters/firestore/repository_event.go b/service/adapters/firestore/repository_event.go index 1ba7849..9327c2d 100644 --- a/service/adapters/firestore/repository_event.go +++ b/service/adapters/firestore/repository_event.go @@ -128,12 +128,16 @@ func (e *EventRepository) saveUnderEvents(event domain.Event) error { // DeleteByPublicKey deletes all events and associated notifications for a given public key. func (e *EventRepository) DeleteByPublicKey(ctx context.Context, pubkey domain.PublicKey) error { - eventsQuery := e.client.Collection(collectionEvents).Where(eventFieldPublicKey, "==", pubkey.Hex()) + notificationsToDelete := make(map[*firestore.DocumentRef][]*firestore.DocumentRef) - eventsDocs := e.tx.Documents(eventsQuery) + eventsQuery := e.client.Collection(collectionEvents).Where(eventFieldPublicKey, "==", pubkey.Hex()) + eventsIter := eventsQuery.Documents(ctx) + // Collect event references and their associated notifications first to + // avoid mixing reads and writes in the same transaction and avoid the + // firestore error "read after write in transaction" for { - eventDoc, err := eventsDocs.Next() + eventDoc, err := eventsIter.Next() if err == iterator.Done { break } @@ -142,23 +146,31 @@ func (e *EventRepository) DeleteByPublicKey(ctx context.Context, pubkey domain.P } notificationsCollection := eventDoc.Ref.Collection(collectionEventsNotifications) - notificationsDocs := e.tx.Documents(notificationsCollection) + notificationsIter := notificationsCollection.Documents(ctx) + var notificationRefs []*firestore.DocumentRef for { - notificationDoc, err := notificationsDocs.Next() + notificationDoc, err := notificationsIter.Next() if err == iterator.Done { break } if err != nil { return errors.Wrap(err, "error fetching notification document") } + notificationRefs = append(notificationRefs, notificationDoc.Ref) + } + + notificationsToDelete[eventDoc.Ref] = notificationRefs + } - if err := e.tx.Delete(notificationDoc.Ref); err != nil { + for eventRef, notificationRefs := range notificationsToDelete { + for _, notificationRef := range notificationRefs { + if err := e.tx.Delete(notificationRef); err != nil { return errors.Wrap(err, "error deleting notification document") } } - if err := e.tx.Delete(eventDoc.Ref); err != nil { + if err := e.tx.Delete(eventRef); err != nil { return errors.Wrap(err, "error deleting event document") } }