a big bundle of conversions and other changes.

This commit is contained in:
fiatjaf
2025-04-15 17:13:57 -03:00
parent f493293be2
commit 2b5b646a62
92 changed files with 852 additions and 2136 deletions
+9 -10
View File
@@ -1,17 +1,16 @@
package badger
import (
"context"
"encoding/binary"
"log"
"github.com/dgraph-io/badger/v4"
bin "fiatjaf.com/nostr/eventstore/internal/binary"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore/codec/betterbinary"
"fiatjaf.com/nostr/nip45/hyperloglog"
"github.com/dgraph-io/badger/v4"
)
func (b *BadgerBackend) CountEvents(ctx context.Context, filter nostr.Filter) (int64, error) {
func (b *BadgerBackend) CountEvents(filter nostr.Filter) (int64, error) {
var count int64 = 0
queries, extraFilter, since, err := prepareQueries(filter)
@@ -62,8 +61,8 @@ func (b *BadgerBackend) CountEvents(ctx context.Context, filter nostr.Filter) (i
}
err = item.Value(func(val []byte) error {
evt := &nostr.Event{}
if err := bin.Unmarshal(val, evt); err != nil {
evt := nostr.Event{}
if err := betterbinary.Unmarshal(val, &evt); err != nil {
return err
}
@@ -87,7 +86,7 @@ func (b *BadgerBackend) CountEvents(ctx context.Context, filter nostr.Filter) (i
return count, err
}
func (b *BadgerBackend) CountEventsHLL(ctx context.Context, filter nostr.Filter, offset int) (int64, *hyperloglog.HyperLogLog, error) {
func (b *BadgerBackend) CountEventsHLL(filter nostr.Filter, offset int) (int64, *hyperloglog.HyperLogLog, error) {
var count int64 = 0
queries, extraFilter, since, err := prepareQueries(filter)
@@ -138,13 +137,13 @@ func (b *BadgerBackend) CountEventsHLL(ctx context.Context, filter nostr.Filter,
err = item.Value(func(val []byte) error {
if extraFilter == nil {
hll.AddBytes(val[32:64])
hll.AddBytes([32]byte(val[32:64]))
count++
return nil
}
evt := &nostr.Event{}
if err := bin.Unmarshal(val, evt); err != nil {
evt := nostr.Event{}
if err := betterbinary.Unmarshal(val, &evt); err != nil {
return err
}
if extraFilter.Matches(evt) {
+16 -8
View File
@@ -1,22 +1,22 @@
package badger
import (
"context"
"encoding/hex"
"fmt"
"log"
"github.com/dgraph-io/badger/v4"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore/codec/betterbinary"
"github.com/dgraph-io/badger/v4"
)
var serialDelete uint32 = 0
func (b *BadgerBackend) DeleteEvent(ctx context.Context, evt *nostr.Event) error {
func (b *BadgerBackend) DeleteEvent(id nostr.ID) error {
deletionHappened := false
err := b.Update(func(txn *badger.Txn) error {
var err error
deletionHappened, err = b.delete(txn, evt)
deletionHappened, err = b.delete(txn, id)
return err
})
if err != nil {
@@ -36,22 +36,30 @@ func (b *BadgerBackend) DeleteEvent(ctx context.Context, evt *nostr.Event) error
return nil
}
func (b *BadgerBackend) delete(txn *badger.Txn, evt *nostr.Event) (bool, error) {
func (b *BadgerBackend) delete(txn *badger.Txn, id nostr.ID) (bool, error) {
idx := make([]byte, 1, 5)
idx[0] = rawEventStorePrefix
// query event by id to get its idx
idPrefix8, _ := hex.DecodeString(evt.ID[0 : 8*2])
prefix := make([]byte, 1+8)
prefix[0] = indexIdPrefix
copy(prefix[1:], idPrefix8)
copy(prefix[1:], id[0:8])
opts := badger.IteratorOptions{
PrefetchValues: false,
}
// also grab the actual event so we can calculate its indexes
var evt nostr.Event
it := txn.NewIterator(opts)
it.Seek(prefix)
if it.ValidForPrefix(prefix) {
idx = append(idx, it.Item().Key()[1+8:]...)
if err := it.Item().Value(func(val []byte) error {
return betterbinary.Unmarshal(val, &evt)
}); err != nil {
return false, fmt.Errorf("failed to unmarshal event %x to delete: %w", id[:], err)
}
}
it.Close()
+5 -8
View File
@@ -40,14 +40,13 @@ func getTagIndexPrefix(tagValue string) ([]byte, int) {
return k, offset
}
func (b *BadgerBackend) getIndexKeysForEvent(evt *nostr.Event, idx []byte) iter.Seq[[]byte] {
func (b *BadgerBackend) getIndexKeysForEvent(evt nostr.Event, idx []byte) iter.Seq[[]byte] {
return func(yield func([]byte) bool) {
{
// ~ by id
idPrefix8, _ := hex.DecodeString(evt.ID[0 : 8*2])
k := make([]byte, 1+8+4)
k[0] = indexIdPrefix
copy(k[1:], idPrefix8)
copy(k[1:], evt.ID[0:8])
copy(k[1+8:], idx)
if !yield(k) {
return
@@ -56,10 +55,9 @@ func (b *BadgerBackend) getIndexKeysForEvent(evt *nostr.Event, idx []byte) iter.
{
// ~ by pubkey+date
pubkeyPrefix8, _ := hex.DecodeString(evt.PubKey[0 : 8*2])
k := make([]byte, 1+8+4+4)
k[0] = indexPubkeyPrefix
copy(k[1:], pubkeyPrefix8)
copy(k[1:], evt.PubKey[0:8])
binary.BigEndian.PutUint32(k[1+8:], uint32(evt.CreatedAt))
copy(k[1+8+4:], idx)
if !yield(k) {
@@ -81,10 +79,9 @@ func (b *BadgerBackend) getIndexKeysForEvent(evt *nostr.Event, idx []byte) iter.
{
// ~ by pubkey+kind+date
pubkeyPrefix8, _ := hex.DecodeString(evt.PubKey[0 : 8*2])
k := make([]byte, 1+8+2+4+4)
k[0] = indexPubkeyKindPrefix
copy(k[1:], pubkeyPrefix8)
copy(k[1:], evt.PubKey[0:8])
binary.BigEndian.PutUint16(k[1+8:], uint16(evt.Kind))
binary.BigEndian.PutUint32(k[1+8+2:], uint32(evt.CreatedAt))
copy(k[1+8+2+4:], idx)
@@ -152,7 +149,7 @@ func getAddrTagElements(tagValue string) (kind uint16, pkb []byte, d string) {
return 0, nil, ""
}
func filterMatchesTags(ef *nostr.Filter, event *nostr.Event) bool {
func filterMatchesTags(ef nostr.Filter, event nostr.Event) bool {
for f, v := range ef.Tags {
if v != nil && !event.Tags.ContainsAny(f, v) {
return false
+4 -4
View File
@@ -5,9 +5,9 @@ import (
"fmt"
"sync/atomic"
"github.com/dgraph-io/badger/v4"
"fiatjaf.com/nostr/eventstore"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore"
"github.com/dgraph-io/badger/v4"
)
const (
@@ -32,9 +32,9 @@ type BadgerBackend struct {
BadgerOptionsModifier func(badger.Options) badger.Options
// Experimental
SkipIndexingTag func(event *nostr.Event, tagName string, tagValue string) bool
SkipIndexingTag func(event nostr.Event, tagName string, tagValue string) bool
// Experimental
IndexLongerTag func(event *nostr.Event, tagName string, tagValue string) bool
IndexLongerTag func(event nostr.Event, tagName string, tagValue string) bool
*badger.DB
+3 -27
View File
@@ -2,7 +2,6 @@ package badger
import (
"encoding/binary"
"fmt"
"github.com/dgraph-io/badger/v4"
)
@@ -26,35 +25,12 @@ func (b *BadgerBackend) runMigrations() error {
// do the migrations in increasing steps (there is no rollback)
//
// the 3 first migrations go to trash because on version 3 we need to export and import all the data anyway
if version < 3 {
// if there is any data in the relay we will stop and notify the user,
// otherwise we just set version to 3 and proceed
prefix := []byte{indexIdPrefix}
it := txn.NewIterator(badger.IteratorOptions{
PrefetchValues: true,
PrefetchSize: 100,
Prefix: prefix,
})
defer it.Close()
hasAnyEntries := false
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
hasAnyEntries = true
break
}
if hasAnyEntries {
return fmt.Errorf("your database is at version %d, but in order to migrate up to version 3 you must manually export all the events and then import again: run an old version of this software, export the data, then delete the database files, run the new version, import the data back in.", version)
}
b.bumpVersion(txn, 3)
}
if version < 4 {
if version < 1 {
// ...
}
// b.bumpVersion(txn, 1)
return nil
})
}
+83 -97
View File
@@ -1,68 +1,54 @@
package badger
import (
"context"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"iter"
"log"
"github.com/dgraph-io/badger/v4"
"fiatjaf.com/nostr/eventstore"
"fiatjaf.com/nostr/eventstore/internal"
bin "fiatjaf.com/nostr/eventstore/internal/binary"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore/codec/betterbinary"
"fiatjaf.com/nostr/eventstore/internal"
"github.com/dgraph-io/badger/v4"
"golang.org/x/exp/slices"
)
var batchFilled = errors.New("batch-filled")
func (b *BadgerBackend) QueryEvents(ctx context.Context, filter nostr.Filter) (chan *nostr.Event, error) {
ch := make(chan *nostr.Event)
if filter.Search != "" {
close(ch)
return ch, nil
}
// max number of events we'll return
maxLimit := b.MaxLimit
var limit int
if eventstore.IsNegentropySession(ctx) {
maxLimit = b.MaxLimitNegentropy
limit = maxLimit
} else {
limit = maxLimit / 4
}
if filter.Limit > 0 && filter.Limit <= maxLimit {
limit = filter.Limit
}
if tlimit := nostr.GetTheoreticalLimit(filter); tlimit == 0 {
close(ch)
return ch, nil
} else if tlimit > 0 {
limit = tlimit
}
// fmt.Println("limit", limit)
go b.View(func(txn *badger.Txn) error {
defer close(ch)
results, err := b.query(txn, filter, limit)
if err != nil {
return err
func (b *BadgerBackend) QueryEvents(filter nostr.Filter) iter.Seq[nostr.Event] {
return func(yield func(nostr.Event) bool) {
if filter.Search != "" {
return
}
for _, evt := range results {
ch <- evt.Event
// max number of events we'll return
limit := b.MaxLimit / 4
if filter.Limit > 0 && filter.Limit <= b.MaxLimit {
limit = filter.Limit
}
if tlimit := nostr.GetTheoreticalLimit(filter); tlimit == 0 {
return
} else if tlimit > 0 {
limit = tlimit
}
return nil
})
// fmt.Println("limit", limit)
b.View(func(txn *badger.Txn) error {
results, err := b.query(txn, filter, limit)
if err != nil {
return err
}
return ch, nil
for _, evt := range results {
if !yield(evt.Event) {
return nil
}
}
return nil
})
}
}
func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) ([]internal.IterEvent, error) {
@@ -81,16 +67,16 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
// we will continue to pull from it as soon as some other iterator takes the position
oldest := internal.IterEvent{Q: -1}
secondPhase := false // after we have gathered enough events we will change the way we iterate
sndPhase := false // after we have gathered enough events we will change the way we iterate
secondBatch := make([][]internal.IterEvent, 0, len(queries)+1)
secondPhaseParticipants := make([]int, 0, len(queries)+1)
sndPhaseParticipants := make([]int, 0, len(queries)+1)
// while merging results in the second phase we will alternate between these two lists
// to avoid having to create new lists all the time
var secondPhaseResultsA []internal.IterEvent
var secondPhaseResultsB []internal.IterEvent
var secondPhaseResultsToggle bool // this is just a dummy thing we use to keep track of the alternating
var secondPhaseHasResultsPending bool
var sndPhaseResultsA []internal.IterEvent
var sndPhaseResultsB []internal.IterEvent
var sndPhaseResultsToggle bool // this is just a dummy thing we use to keep track of the alternating
var sndPhaseHasResultsPending bool
remainingUnexhausted := len(queries) // when all queries are exhausted we can finally end this thing
batchSizePerQuery := internal.BatchSizePerNumberOfQueries(limit, remainingUnexhausted)
@@ -180,26 +166,26 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
// check it against pubkeys without decoding the entire thing
if extraFilter != nil && extraFilter.Authors != nil &&
!slices.Contains(extraFilter.Authors, hex.EncodeToString(val[32:64])) {
!nostr.ContainsPubKey(extraFilter.Authors, nostr.PubKey(val[32:64])) {
// fmt.Println(" skipped (authors)")
return nil
}
// check it against kinds without decoding the entire thing
if extraFilter != nil && extraFilter.Kinds != nil &&
!slices.Contains(extraFilter.Kinds, int(binary.BigEndian.Uint16(val[132:134]))) {
!slices.Contains(extraFilter.Kinds, binary.BigEndian.Uint16(val[132:134])) {
// fmt.Println(" skipped (kinds)")
return nil
}
event := &nostr.Event{}
if err := bin.Unmarshal(val, event); err != nil {
event := nostr.Event{}
if err := betterbinary.Unmarshal(val, &event); err != nil {
log.Printf("badger: value read error (id %x): %s\n", val[0:32], err)
return err
}
// check if this matches the other filters that were not part of the index
if extraFilter != nil && !filterMatchesTags(extraFilter, event) {
if extraFilter != nil && !filterMatchesTags(*extraFilter, event) {
// fmt.Println(" skipped (filter)", extraFilter, event)
return nil
}
@@ -208,18 +194,18 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
evt := internal.IterEvent{Event: event, Q: q}
//
//
if secondPhase {
if sndPhase {
// do the process described below at HIWAWVRTP.
// if we've reached here this means we've already passed the `since` check.
// now we have to eliminate the event currently at the `since` threshold.
nextThreshold := firstPhaseResults[len(firstPhaseResults)-2]
if oldest.Event == nil {
if oldest.Event.ID == nostr.ZeroID {
// fmt.Println(" b1")
// BRANCH WHEN WE DON'T HAVE THE OLDEST EVENT (BWWDHTOE)
// when we don't have the oldest set, we will keep the results
// and not change the cutting point -- it's bad, but hopefully not that bad.
results[q] = append(results[q], evt)
secondPhaseHasResultsPending = true
sndPhaseHasResultsPending = true
} else if nextThreshold.CreatedAt > oldest.CreatedAt {
// fmt.Println(" b2", nextThreshold.CreatedAt, ">", oldest.CreatedAt)
// one of the events we have stored is the actual next threshold
@@ -236,7 +222,7 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
// finally
// add this to the results to be merged later
results[q] = append(results[q], evt)
secondPhaseHasResultsPending = true
sndPhaseHasResultsPending = true
} else if nextThreshold.CreatedAt < evt.CreatedAt {
// the next last event in the firstPhaseResults is the next threshold
// fmt.Println(" b3", nextThreshold.CreatedAt, "<", oldest.CreatedAt)
@@ -246,7 +232,7 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
// fmt.Println(" new since", since)
// add this to the results to be merged later
results[q] = append(results[q], evt)
secondPhaseHasResultsPending = true
sndPhaseHasResultsPending = true
// update the oldest event
if evt.CreatedAt < oldest.CreatedAt {
oldest = evt
@@ -265,7 +251,7 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
firstPhaseTotalPulled++
// update the oldest event
if oldest.Event == nil || evt.CreatedAt < oldest.CreatedAt {
if oldest.Event.ID == nostr.ZeroID || evt.CreatedAt < oldest.CreatedAt {
oldest = evt
}
}
@@ -295,20 +281,20 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
// we will do this check if we don't accumulated the requested number of events yet
// fmt.Println("oldest", oldest.Event, "from iter", oldest.Q)
if secondPhase && secondPhaseHasResultsPending && (oldest.Event == nil || remainingUnexhausted == 0) {
if sndPhase && sndPhaseHasResultsPending && (oldest.Event.ID == nostr.ZeroID || remainingUnexhausted == 0) {
// fmt.Println("second phase aggregation!")
// when we are in the second phase we will aggressively aggregate results on every iteration
//
secondBatch = secondBatch[:0]
for s := 0; s < len(secondPhaseParticipants); s++ {
q := secondPhaseParticipants[s]
for s := 0; s < len(sndPhaseParticipants); s++ {
q := sndPhaseParticipants[s]
if len(results[q]) > 0 {
secondBatch = append(secondBatch, results[q])
}
if exhausted[q] {
secondPhaseParticipants = internal.SwapDelete(secondPhaseParticipants, s)
sndPhaseParticipants = internal.SwapDelete(sndPhaseParticipants, s)
s--
}
}
@@ -316,29 +302,29 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
// every time we get here we will alternate between these A and B lists
// combining everything we have into a new partial results list.
// after we've done that we can again set the oldest.
// fmt.Println(" xxx", secondPhaseResultsToggle)
if secondPhaseResultsToggle {
secondBatch = append(secondBatch, secondPhaseResultsB)
secondPhaseResultsA = internal.MergeSortMultiple(secondBatch, limit, secondPhaseResultsA)
oldest = secondPhaseResultsA[len(secondPhaseResultsA)-1]
// fmt.Println(" new aggregated a", len(secondPhaseResultsB))
// fmt.Println(" xxx", sndPhaseResultsToggle)
if sndPhaseResultsToggle {
secondBatch = append(secondBatch, sndPhaseResultsB)
sndPhaseResultsA = internal.MergeSortMultiple(secondBatch, limit, sndPhaseResultsA)
oldest = sndPhaseResultsA[len(sndPhaseResultsA)-1]
// fmt.Println(" new aggregated a", len(sndPhaseResultsB))
} else {
secondBatch = append(secondBatch, secondPhaseResultsA)
secondPhaseResultsB = internal.MergeSortMultiple(secondBatch, limit, secondPhaseResultsB)
oldest = secondPhaseResultsB[len(secondPhaseResultsB)-1]
// fmt.Println(" new aggregated b", len(secondPhaseResultsB))
secondBatch = append(secondBatch, sndPhaseResultsA)
sndPhaseResultsB = internal.MergeSortMultiple(secondBatch, limit, sndPhaseResultsB)
oldest = sndPhaseResultsB[len(sndPhaseResultsB)-1]
// fmt.Println(" new aggregated b", len(sndPhaseResultsB))
}
secondPhaseResultsToggle = !secondPhaseResultsToggle
sndPhaseResultsToggle = !sndPhaseResultsToggle
since = uint32(oldest.CreatedAt)
// fmt.Println(" new since", since)
// reset the `results` list so we can keep using it
results = results[:len(queries)]
for _, q := range secondPhaseParticipants {
for _, q := range sndPhaseParticipants {
results[q] = results[q][:0]
}
} else if !secondPhase && firstPhaseTotalPulled >= limit && remainingUnexhausted > 0 {
} else if !sndPhase && firstPhaseTotalPulled >= limit && remainingUnexhausted > 0 {
// fmt.Println("have enough!", firstPhaseTotalPulled, "/", limit, "remaining", remainingUnexhausted)
// we will exclude this oldest number as it is not relevant anymore
@@ -382,16 +368,16 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
results[q] = results[q][:0]
// build this index of indexes with everybody who remains
secondPhaseParticipants = append(secondPhaseParticipants, q)
sndPhaseParticipants = append(sndPhaseParticipants, q)
}
// we create these two lists and alternate between them so we don't have to create a
// a new one every time
secondPhaseResultsA = make([]internal.IterEvent, 0, limit*2)
secondPhaseResultsB = make([]internal.IterEvent, 0, limit*2)
sndPhaseResultsA = make([]internal.IterEvent, 0, limit*2)
sndPhaseResultsB = make([]internal.IterEvent, 0, limit*2)
// from now on we won't run this block anymore
secondPhase = true
sndPhase = true
}
// fmt.Println("remaining", remainingUnexhausted)
@@ -400,27 +386,27 @@ func (b *BadgerBackend) query(txn *badger.Txn, filter nostr.Filter, limit int) (
}
}
// fmt.Println("is secondPhase?", secondPhase)
// fmt.Println("is sndPhase?", sndPhase)
var combinedResults []internal.IterEvent
if secondPhase {
if sndPhase {
// fmt.Println("ending second phase")
// when we reach this point either secondPhaseResultsA or secondPhaseResultsB will be full of stuff,
// when we reach this point either sndPhaseResultsA or sndPhaseResultsB will be full of stuff,
// the other will be empty
var secondPhaseResults []internal.IterEvent
// fmt.Println("xxx", secondPhaseResultsToggle, len(secondPhaseResultsA), len(secondPhaseResultsB))
if secondPhaseResultsToggle {
secondPhaseResults = secondPhaseResultsB
combinedResults = secondPhaseResultsA[0:limit] // reuse this
// fmt.Println(" using b", len(secondPhaseResultsA))
var sndPhaseResults []internal.IterEvent
// fmt.Println("xxx", sndPhaseResultsToggle, len(sndPhaseResultsA), len(sndPhaseResultsB))
if sndPhaseResultsToggle {
sndPhaseResults = sndPhaseResultsB
combinedResults = sndPhaseResultsA[0:limit] // reuse this
// fmt.Println(" using b", len(sndPhaseResultsA))
} else {
secondPhaseResults = secondPhaseResultsA
combinedResults = secondPhaseResultsB[0:limit] // reuse this
// fmt.Println(" using a", len(secondPhaseResultsA))
sndPhaseResults = sndPhaseResultsA
combinedResults = sndPhaseResultsB[0:limit] // reuse this
// fmt.Println(" using a", len(sndPhaseResultsA))
}
all := [][]internal.IterEvent{firstPhaseResults, secondPhaseResults}
all := [][]internal.IterEvent{firstPhaseResults, sndPhaseResults}
combinedResults = internal.MergeSortMultiple(all, limit, combinedResults)
// fmt.Println("final combinedResults", len(combinedResults), cap(combinedResults), limit)
} else {
+5 -6
View File
@@ -1,23 +1,22 @@
package badger
import (
"context"
"fmt"
"math"
"github.com/dgraph-io/badger/v4"
"fiatjaf.com/nostr/eventstore/internal"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore/internal"
"github.com/dgraph-io/badger/v4"
)
func (b *BadgerBackend) ReplaceEvent(ctx context.Context, evt *nostr.Event) error {
func (b *BadgerBackend) ReplaceEvent(evt nostr.Event) error {
// sanity checking
if evt.CreatedAt > math.MaxUint32 || evt.Kind > math.MaxUint16 {
return fmt.Errorf("event with values out of expected boundaries")
}
return b.Update(func(txn *badger.Txn) error {
filter := nostr.Filter{Limit: 1, Kinds: []int{evt.Kind}, Authors: []string{evt.PubKey}}
filter := nostr.Filter{Limit: 1, Kinds: []uint16{evt.Kind}, Authors: []nostr.PubKey{evt.PubKey}}
if nostr.IsAddressableKind(evt.Kind) {
// when addressable, add the "d" tag to the filter
filter.Tags = nostr.TagMap{"d": []string{evt.Tags.GetD()}}
@@ -32,7 +31,7 @@ func (b *BadgerBackend) ReplaceEvent(ctx context.Context, evt *nostr.Event) erro
shouldStore := true
for _, previous := range results {
if internal.IsOlder(previous.Event, evt) {
if _, err := b.delete(txn, previous.Event); err != nil {
if _, err := b.delete(txn, previous.Event.ID); err != nil {
return fmt.Errorf("failed to delete event %s for replacing: %w", previous.Event.ID, err)
}
} else {
+9 -12
View File
@@ -1,18 +1,16 @@
package badger
import (
"context"
"encoding/hex"
"fmt"
"math"
"github.com/dgraph-io/badger/v4"
"fiatjaf.com/nostr/eventstore"
bin "fiatjaf.com/nostr/eventstore/internal/binary"
"fiatjaf.com/nostr"
"fiatjaf.com/nostr/eventstore"
"fiatjaf.com/nostr/eventstore/codec/betterbinary"
"github.com/dgraph-io/badger/v4"
)
func (b *BadgerBackend) SaveEvent(ctx context.Context, evt *nostr.Event) error {
func (b *BadgerBackend) SaveEvent(evt nostr.Event) error {
// sanity checking
if evt.CreatedAt > math.MaxUint32 || evt.Kind > math.MaxUint16 {
return fmt.Errorf("event with values out of expected boundaries")
@@ -20,10 +18,9 @@ func (b *BadgerBackend) SaveEvent(ctx context.Context, evt *nostr.Event) error {
return b.Update(func(txn *badger.Txn) error {
// query event by id to ensure we don't save duplicates
id, _ := hex.DecodeString(evt.ID)
prefix := make([]byte, 1+8)
prefix[0] = indexIdPrefix
copy(prefix[1:], id)
copy(prefix[1:], evt.ID[0:8])
it := txn.NewIterator(badger.IteratorOptions{})
defer it.Close()
it.Seek(prefix)
@@ -36,16 +33,16 @@ func (b *BadgerBackend) SaveEvent(ctx context.Context, evt *nostr.Event) error {
})
}
func (b *BadgerBackend) save(txn *badger.Txn, evt *nostr.Event) error {
func (b *BadgerBackend) save(txn *badger.Txn, evt nostr.Event) error {
// encode to binary
bin, err := bin.Marshal(evt)
if err != nil {
buf := make([]byte, betterbinary.Measure(evt))
if err := betterbinary.Marshal(evt, buf); err != nil {
return err
}
idx := b.Serial()
// raw event store
if err := txn.Set(idx, bin); err != nil {
if err := txn.Set(idx, buf); err != nil {
return err
}