diff --git a/eventstore/boltdb/halt_test.go b/eventstore/boltdb/halt_test.go new file mode 100644 index 0000000..0d679d9 --- /dev/null +++ b/eventstore/boltdb/halt_test.go @@ -0,0 +1,67 @@ +package boltdb + +import ( + "context" + "log" + "net/http" + "os" + "testing" + "time" + + "fiatjaf.com/nostr" + "fiatjaf.com/nostr/khatru" + "github.com/stretchr/testify/require" +) + +func TestHaltingProblem(t *testing.T) { + go func() { + if err := os.RemoveAll("/tmp/bolthalttest"); err != nil { + log.Fatal(err) + return + } + db := BoltBackend{Path: "/tmp/bolthalttest"} + if err := db.Init(); err != nil { + panic(err) + } + + relay := khatru.NewRelay() + relay.UseEventstore(&db, 500) + + server := &http.Server{Addr: ":54898", Handler: relay} + server.ListenAndServe() + }() + + time.Sleep(time.Millisecond * 200) + client, err := nostr.RelayConnect(t.Context(), "http://127.0.0.1:54898", nostr.RelayOptions{}) + require.NoError(t, err) + sk := nostr.Generate() + var id nostr.ID + + { + evt := nostr.Event{ + CreatedAt: nostr.Now(), + Content: "", + Kind: nostr.Kind(1), + } + evt.Sign(sk) + err := client.Publish(context.Background(), evt) + require.NoError(t, err) + id = evt.ID + t.Logf("event published: %s\n", id.Hex()) + } + + { + evt := nostr.Event{ + CreatedAt: nostr.Now(), + Tags: nostr.Tags{ + nostr.Tag{"e", id.Hex()}, + }, + Kind: nostr.Kind(5), + } + evt.Sign(sk) + + err := client.Publish(context.Background(), evt) + require.NoError(t, err) + t.Logf("event deleted\n") + } +} diff --git a/khatru/deleting.go b/khatru/deleting.go index 7ac9ca0..69c2d51 100644 --- a/khatru/deleting.go +++ b/khatru/deleting.go @@ -8,6 +8,7 @@ import ( "strings" "fiatjaf.com/nostr" + "golang.org/x/sync/errgroup" ) var ( @@ -61,20 +62,24 @@ func (rl *Relay) handleDeleteRequest(ctx context.Context, evt nostr.Event) error ctx := context.WithValue(ctx, internalCallKey, struct{}{}) + errg, ctx := errgroup.WithContext(ctx) for target := range rl.QueryStored(ctx, f) { // got the event, now check if the user can delete it if target.PubKey == evt.PubKey { // delete it - if err := rl.DeleteEvent(ctx, target.ID); err != nil { - return err - } + errg.Go(func() error { + if err := rl.DeleteEvent(ctx, target.ID); err != nil { + return err + } - // if it was tracked to be expired that is not needed anymore - if rl.expirationManager != nil { - rl.expirationManager.removeEvent(target.ID) - } + // if it was tracked to be expired that is not needed anymore + if rl.expirationManager != nil { + rl.expirationManager.removeEvent(target.ID) + } - haveDeletedSomething = true + haveDeletedSomething = true + return nil + }) } else { // fail and stop here return ErrNotAuthor @@ -83,6 +88,10 @@ func (rl *Relay) handleDeleteRequest(ctx context.Context, evt nostr.Event) error // don't try to query this same event again break } + + if err := errg.Wait(); err != nil { + return err + } } }