Browse Source

refactor save event method to expose whether it replaced an event

main
mleku 3 months ago
parent
commit
bc5177e0ec
No known key found for this signature in database
  1. 51
      app/handle-event.go
  2. 116
      app/payment_processor.go
  3. 8
      cmd/benchmark/main.go
  4. 2
      pkg/acl/follows.go
  5. 2
      pkg/database/export_test.go
  6. 2
      pkg/database/fetch-event-by-serial_test.go
  7. 2
      pkg/database/get-serial-by-id_test.go
  8. 2
      pkg/database/get-serials-by-range_test.go
  9. 2
      pkg/database/import.go
  10. 6
      pkg/database/query-events-multiple-param-replaceable_test.go
  11. 364
      pkg/database/query-events-search_test.go
  12. 14
      pkg/database/query-events_test.go
  13. 2
      pkg/database/query-for-authors-tags_test.go
  14. 2
      pkg/database/query-for-ids_test.go
  15. 2
      pkg/database/query-for-kinds-authors-tags_test.go
  16. 2
      pkg/database/query-for-kinds-authors_test.go
  17. 2
      pkg/database/query-for-kinds-tags_test.go
  18. 2
      pkg/database/query-for-kinds_test.go
  19. 2
      pkg/database/query-for-serials_test.go
  20. 2
      pkg/database/query-for-tags_test.go
  21. 26
      pkg/database/save-event.go
  22. 10
      pkg/database/save-event_test.go
  23. 2
      pkg/interfaces/store/store_interface.go
  24. 36
      pkg/spider/spider.go
  25. 2
      pkg/version/version

51
app/handle-event.go

@ -27,7 +27,10 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
log.E.F("HandleEvent: failed to unmarshal event: %v", err) log.E.F("HandleEvent: failed to unmarshal event: %v", err)
return return
} }
log.I.F("HandleEvent: successfully unmarshaled event, kind: %d, pubkey: %s", env.E.Kind, hex.Enc(env.E.Pubkey)) log.I.F(
"HandleEvent: successfully unmarshaled event, kind: %d, pubkey: %s",
env.E.Kind, hex.Enc(env.E.Pubkey),
)
defer func() { defer func() {
if env != nil && env.E != nil { if env != nil && env.E != nil {
env.E.Free() env.E.Free()
@ -45,7 +48,8 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
// Sprocket is disabled due to failure - reject all events // Sprocket is disabled due to failure - reject all events
log.W.F("sprocket is disabled, rejecting event %0x", env.E.ID) log.W.F("sprocket is disabled, rejecting event %0x", env.E.ID)
if err = Ok.Error( if err = Ok.Error(
l, env, "sprocket disabled - events rejected until sprocket is restored", l, env,
"sprocket disabled - events rejected until sprocket is restored",
); chk.E(err) { ); chk.E(err) {
return return
} }
@ -54,9 +58,13 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
if !l.sprocketManager.IsRunning() { if !l.sprocketManager.IsRunning() {
// Sprocket is enabled but not running - reject all events // Sprocket is enabled but not running - reject all events
log.W.F("sprocket is enabled but not running, rejecting event %0x", env.E.ID) log.W.F(
"sprocket is enabled but not running, rejecting event %0x",
env.E.ID,
)
if err = Ok.Error( if err = Ok.Error(
l, env, "sprocket not running - events rejected until sprocket starts", l, env,
"sprocket not running - events rejected until sprocket starts",
); chk.E(err) { ); chk.E(err) {
return return
} }
@ -134,13 +142,19 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
return return
} }
// check permissions of user // check permissions of user
log.I.F("HandleEvent: checking ACL permissions for pubkey: %s", hex.Enc(l.authedPubkey.Load())) log.I.F(
"HandleEvent: checking ACL permissions for pubkey: %s",
hex.Enc(l.authedPubkey.Load()),
)
// If ACL mode is "none" and no pubkey is set, use the event's pubkey // If ACL mode is "none" and no pubkey is set, use the event's pubkey
var pubkeyForACL []byte var pubkeyForACL []byte
if len(l.authedPubkey.Load()) == 0 && acl.Registry.Active.Load() == "none" { if len(l.authedPubkey.Load()) == 0 && acl.Registry.Active.Load() == "none" {
pubkeyForACL = env.E.Pubkey pubkeyForACL = env.E.Pubkey
log.I.F("HandleEvent: ACL mode is 'none', using event pubkey for ACL check: %s", hex.Enc(pubkeyForACL)) log.I.F(
"HandleEvent: ACL mode is 'none', using event pubkey for ACL check: %s",
hex.Enc(pubkeyForACL),
)
} else { } else {
pubkeyForACL = l.authedPubkey.Load() pubkeyForACL = l.authedPubkey.Load()
} }
@ -174,7 +188,8 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
switch accessLevel { switch accessLevel {
case "none": case "none":
log.D.F( log.D.F(
"handle event: sending 'OK,false,auth-required...' to %s", l.remote, "handle event: sending 'OK,false,auth-required...' to %s",
l.remote,
) )
if err = okenvelope.NewFrom( if err = okenvelope.NewFrom(
env.Id(), false, env.Id(), false,
@ -242,16 +257,24 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
} }
} }
// if the event is a delete, process the delete // if the event is a delete, process the delete
log.I.F("HandleEvent: checking if event is delete - kind: %d, EventDeletion.K: %d", env.E.Kind, kind.EventDeletion.K) log.I.F(
"HandleEvent: checking if event is delete - kind: %d, EventDeletion.K: %d",
env.E.Kind, kind.EventDeletion.K,
)
if env.E.Kind == kind.EventDeletion.K { if env.E.Kind == kind.EventDeletion.K {
log.I.F("processing delete event %0x", env.E.ID) log.I.F("processing delete event %0x", env.E.ID)
// Store the delete event itself FIRST to ensure it's available for queries // Store the delete event itself FIRST to ensure it's available for queries
saveCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) saveCtx, cancel := context.WithTimeout(
context.Background(), 30*time.Second,
)
defer cancel() defer cancel()
log.I.F("attempting to save delete event %0x from pubkey %0x", env.E.ID, env.E.Pubkey) log.I.F(
"attempting to save delete event %0x from pubkey %0x", env.E.ID,
env.E.Pubkey,
)
log.I.F("delete event pubkey hex: %s", hex.Enc(env.E.Pubkey)) log.I.F("delete event pubkey hex: %s", hex.Enc(env.E.Pubkey))
if _, _, err = l.SaveEvent(saveCtx, env.E); err != nil { if err, _ = l.SaveEvent(saveCtx, env.E); err != nil {
log.E.F("failed to save delete event %0x: %v", env.E.ID, err) log.E.F("failed to save delete event %0x: %v", env.E.ID, err)
if strings.HasPrefix(err.Error(), "blocked:") { if strings.HasPrefix(err.Error(), "blocked:") {
errStr := err.Error()[len("blocked: "):len(err.Error())] errStr := err.Error()[len("blocked: "):len(err.Error())]
@ -282,7 +305,9 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
// For non-blocked errors, still send OK but log the error // For non-blocked errors, still send OK but log the error
log.W.F("Delete processing failed but continuing: %v", err) log.W.F("Delete processing failed but continuing: %v", err)
} else { } else {
log.I.F("HandleDelete completed successfully for event %0x", env.E.ID) log.I.F(
"HandleDelete completed successfully for event %0x", env.E.ID,
)
} }
// Send OK response for delete events // Send OK response for delete events
@ -314,7 +339,7 @@ func (l *Listener) HandleEvent(msg []byte) (err error) {
saveCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) saveCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
// log.I.F("saving event %0x, %s", env.E.ID, env.E.Serialize()) // log.I.F("saving event %0x, %s", env.E.ID, env.E.Serialize())
if _, _, err = l.SaveEvent(saveCtx, env.E); err != nil { if err, _ = l.SaveEvent(saveCtx, env.E); err != nil {
if strings.HasPrefix(err.Error(), "blocked:") { if strings.HasPrefix(err.Error(), "blocked:") {
errStr := err.Error()[len("blocked: "):len(err.Error())] errStr := err.Error()[len("blocked: "):len(err.Error())]
if err = Ok.Error( if err = Ok.Error(

116
app/payment_processor.go

@ -166,7 +166,7 @@ func (pp *PaymentProcessor) syncFollowList() error {
} }
// sign and save // sign and save
ev.Sign(sign) ev.Sign(sign)
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil { if err, _ := pp.db.SaveEvent(pp.ctx, ev); err != nil {
return err return err
} }
log.I.F( log.I.F(
@ -261,7 +261,9 @@ func (pp *PaymentProcessor) checkSubscriptionStatus() error {
} }
// createExpiryWarningNote creates a warning note for users whose paid subscription expires in 7 days // createExpiryWarningNote creates a warning note for users whose paid subscription expires in 7 days
func (pp *PaymentProcessor) createExpiryWarningNote(userPubkey []byte, expiryTime time.Time) error { func (pp *PaymentProcessor) createExpiryWarningNote(
userPubkey []byte, expiryTime time.Time,
) error {
// Get relay identity secret to sign the note // Get relay identity secret to sign the note
skb, err := pp.db.GetRelayIdentitySecret() skb, err := pp.db.GetRelayIdentitySecret()
if err != nil || len(skb) != 32 { if err != nil || len(skb) != 32 {
@ -286,7 +288,8 @@ func (pp *PaymentProcessor) createExpiryWarningNote(userPubkey []byte, expiryTim
} }
// Create the warning note content // Create the warning note content
content := fmt.Sprintf(` Subscription Expiring Soon content := fmt.Sprintf(
` Subscription Expiring Soon
Your paid subscription to this relay will expire in 7 days on %s. Your paid subscription to this relay will expire in 7 days on %s.
@ -305,7 +308,9 @@ Don't lose access to your private relay! Extend your subscription today.
Relay: nostr:%s Relay: nostr:%s
Log in to the relay dashboard to access your configuration at: %s`, Log in to the relay dashboard to access your configuration at: %s`,
expiryTime.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, monthlyPrice, string(relayNpubForContent), pp.getDashboardURL()) expiryTime.Format("2006-01-02 15:04:05 UTC"), monthlyPrice,
monthlyPrice, string(relayNpubForContent), pp.getDashboardURL(),
)
// Build the event // Build the event
ev := event.New() ev := event.New()
@ -320,7 +325,10 @@ Log in to the relay dashboard to access your configuration at: %s`,
// Add expiration tag (5 days from creation) // Add expiration tag (5 days from creation)
noteExpiry := time.Now().AddDate(0, 0, 5) noteExpiry := time.Now().AddDate(0, 0, 5)
*ev.Tags = append(*ev.Tags, tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix()))) *ev.Tags = append(
*ev.Tags,
tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix())),
)
// Add "private" tag with authorized npubs (user and relay) // Add "private" tag with authorized npubs (user and relay)
var authorizedNpubs []string var authorizedNpubs []string
@ -344,20 +352,27 @@ Log in to the relay dashboard to access your configuration at: %s`,
} }
// Add a special tag to mark this as an expiry warning // Add a special tag to mark this as an expiry warning
*ev.Tags = append(*ev.Tags, tag.NewFromAny("warning", "subscription-expiry")) *ev.Tags = append(
*ev.Tags, tag.NewFromAny("warning", "subscription-expiry"),
)
// Sign and save the event // Sign and save the event
ev.Sign(sign) ev.Sign(sign)
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil { if err, _ := pp.db.SaveEvent(pp.ctx, ev); err != nil {
return fmt.Errorf("failed to save expiry warning note: %w", err) return fmt.Errorf("failed to save expiry warning note: %w", err)
} }
log.I.F("created expiry warning note for user %s (expires %s)", hex.Enc(userPubkey), expiryTime.Format("2006-01-02")) log.I.F(
"created expiry warning note for user %s (expires %s)",
hex.Enc(userPubkey), expiryTime.Format("2006-01-02"),
)
return nil return nil
} }
// createTrialReminderNote creates a reminder note for users on trial to support the relay // createTrialReminderNote creates a reminder note for users on trial to support the relay
func (pp *PaymentProcessor) createTrialReminderNote(userPubkey []byte, trialEnd time.Time) error { func (pp *PaymentProcessor) createTrialReminderNote(
userPubkey []byte, trialEnd time.Time,
) error {
// Get relay identity secret to sign the note // Get relay identity secret to sign the note
skb, err := pp.db.GetRelayIdentitySecret() skb, err := pp.db.GetRelayIdentitySecret()
if err != nil || len(skb) != 32 { if err != nil || len(skb) != 32 {
@ -385,7 +400,8 @@ func (pp *PaymentProcessor) createTrialReminderNote(userPubkey []byte, trialEnd
} }
// Create the reminder note content // Create the reminder note content
content := fmt.Sprintf(`🆓 Free Trial Reminder 🆓 content := fmt.Sprintf(
`🆓 Free Trial Reminder 🆓
You're currently using this relay for FREE! Your trial expires on %s. You're currently using this relay for FREE! Your trial expires on %s.
@ -408,7 +424,9 @@ Thank you for considering supporting decentralized communication!
Relay: nostr:%s Relay: nostr:%s
Log in to the relay dashboard to access your configuration at: %s`, Log in to the relay dashboard to access your configuration at: %s`,
trialEnd.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, dailyRate, monthlyPrice, string(relayNpubForContent), pp.getDashboardURL()) trialEnd.Format("2006-01-02 15:04:05 UTC"), monthlyPrice, dailyRate,
monthlyPrice, string(relayNpubForContent), pp.getDashboardURL(),
)
// Build the event // Build the event
ev := event.New() ev := event.New()
@ -423,7 +441,10 @@ Log in to the relay dashboard to access your configuration at: %s`,
// Add expiration tag (5 days from creation) // Add expiration tag (5 days from creation)
noteExpiry := time.Now().AddDate(0, 0, 5) noteExpiry := time.Now().AddDate(0, 0, 5)
*ev.Tags = append(*ev.Tags, tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix()))) *ev.Tags = append(
*ev.Tags,
tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix())),
)
// Add "private" tag with authorized npubs (user and relay) // Add "private" tag with authorized npubs (user and relay)
var authorizedNpubs []string var authorizedNpubs []string
@ -451,11 +472,14 @@ Log in to the relay dashboard to access your configuration at: %s`,
// Sign and save the event // Sign and save the event
ev.Sign(sign) ev.Sign(sign)
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil { if err, _ := pp.db.SaveEvent(pp.ctx, ev); err != nil {
return fmt.Errorf("failed to save trial reminder note: %w", err) return fmt.Errorf("failed to save trial reminder note: %w", err)
} }
log.I.F("created trial reminder note for user %s (trial ends %s)", hex.Enc(userPubkey), trialEnd.Format("2006-01-02")) log.I.F(
"created trial reminder note for user %s (trial ends %s)",
hex.Enc(userPubkey), trialEnd.Format("2006-01-02"),
)
return nil return nil
} }
@ -501,8 +525,13 @@ func (pp *PaymentProcessor) handleNotification(
if skb, err := pp.db.GetRelayIdentitySecret(); err == nil && len(skb) == 32 { if skb, err := pp.db.GetRelayIdentitySecret(); err == nil && len(skb) == 32 {
var signer p256k.Signer var signer p256k.Signer
if err := signer.InitSec(skb); err == nil { if err := signer.InitSec(skb); err == nil {
if !strings.EqualFold(hex.Enc(rpk), hex.Enc(signer.Pub())) { if !strings.EqualFold(
log.W.F("relay_pubkey in payment metadata does not match this relay identity: got %s want %s", hex.Enc(rpk), hex.Enc(signer.Pub())) hex.Enc(rpk), hex.Enc(signer.Pub()),
) {
log.W.F(
"relay_pubkey in payment metadata does not match this relay identity: got %s want %s",
hex.Enc(rpk), hex.Enc(signer.Pub()),
)
} }
} }
} }
@ -557,9 +586,15 @@ func (pp *PaymentProcessor) handleNotification(
// Log helpful identifiers // Log helpful identifiers
var payerHex = hex.Enc(pubkey) var payerHex = hex.Enc(pubkey)
if userNpub == "" { if userNpub == "" {
log.I.F("payment processed: payer %s %d sats -> %d days", payerHex, satsReceived, days) log.I.F(
"payment processed: payer %s %d sats -> %d days", payerHex,
satsReceived, days,
)
} else { } else {
log.I.F("payment processed: %s (%s) %d sats -> %d days", userNpub, payerHex, satsReceived, days) log.I.F(
"payment processed: %s (%s) %d sats -> %d days", userNpub, payerHex,
satsReceived, days,
)
} }
// Update ACL follows cache and relay follow list immediately // Update ACL follows cache and relay follow list immediately
@ -578,7 +613,9 @@ func (pp *PaymentProcessor) handleNotification(
} }
// createPaymentNote creates a note recording the payment with private tag for authorization // createPaymentNote creates a note recording the payment with private tag for authorization
func (pp *PaymentProcessor) createPaymentNote(payerPubkey []byte, satsReceived int64, days int) error { func (pp *PaymentProcessor) createPaymentNote(
payerPubkey []byte, satsReceived int64, days int,
) error {
// Get relay identity secret to sign the note // Get relay identity secret to sign the note
skb, err := pp.db.GetRelayIdentitySecret() skb, err := pp.db.GetRelayIdentitySecret()
if err != nil || len(skb) != 32 { if err != nil || len(skb) != 32 {
@ -611,8 +648,11 @@ func (pp *PaymentProcessor) createPaymentNote(payerPubkey []byte, satsReceived i
} }
// Create the note content with nostr:npub link and dashboard link // Create the note content with nostr:npub link and dashboard link
content := fmt.Sprintf("Payment received: %d sats for %d days. Subscription expires: %s\n\nRelay: nostr:%s\n\nLog in to the relay dashboard to access your configuration at: %s", content := fmt.Sprintf(
satsReceived, days, expiryTime.Format("2006-01-02 15:04:05 UTC"), string(relayNpubForContent), pp.getDashboardURL()) "Payment received: %d sats for %d days. Subscription expires: %s\n\nRelay: nostr:%s\n\nLog in to the relay dashboard to access your configuration at: %s",
satsReceived, days, expiryTime.Format("2006-01-02 15:04:05 UTC"),
string(relayNpubForContent), pp.getDashboardURL(),
)
// Build the event // Build the event
ev := event.New() ev := event.New()
@ -627,7 +667,10 @@ func (pp *PaymentProcessor) createPaymentNote(payerPubkey []byte, satsReceived i
// Add expiration tag (5 days from creation) // Add expiration tag (5 days from creation)
noteExpiry := time.Now().AddDate(0, 0, 5) noteExpiry := time.Now().AddDate(0, 0, 5)
*ev.Tags = append(*ev.Tags, tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix()))) *ev.Tags = append(
*ev.Tags,
tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix())),
)
// Add "private" tag with authorized npubs (payer and relay) // Add "private" tag with authorized npubs (payer and relay)
var authorizedNpubs []string var authorizedNpubs []string
@ -652,11 +695,14 @@ func (pp *PaymentProcessor) createPaymentNote(payerPubkey []byte, satsReceived i
// Sign and save the event // Sign and save the event
ev.Sign(sign) ev.Sign(sign)
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil { if err, _ := pp.db.SaveEvent(pp.ctx, ev); err != nil {
return fmt.Errorf("failed to save payment note: %w", err) return fmt.Errorf("failed to save payment note: %w", err)
} }
log.I.F("created payment note for %s with private authorization", hex.Enc(payerPubkey)) log.I.F(
"created payment note for %s with private authorization",
hex.Enc(payerPubkey),
)
return nil return nil
} }
@ -686,7 +732,8 @@ func (pp *PaymentProcessor) CreateWelcomeNote(userPubkey []byte) error {
} }
// Create the welcome note content with nostr:npub link // Create the welcome note content with nostr:npub link
content := fmt.Sprintf(`Welcome to the relay! 🎉 content := fmt.Sprintf(
`Welcome to the relay! 🎉
You have a FREE 30-day trial that started when you first logged in. You have a FREE 30-day trial that started when you first logged in.
@ -706,7 +753,9 @@ Relay: nostr:%s
Log in to the relay dashboard to access your configuration at: %s Log in to the relay dashboard to access your configuration at: %s
Enjoy your time on the relay!`, monthlyPrice, monthlyPrice, string(relayNpubForContent), pp.getDashboardURL()) Enjoy your time on the relay!`, monthlyPrice, monthlyPrice,
string(relayNpubForContent), pp.getDashboardURL(),
)
// Build the event // Build the event
ev := event.New() ev := event.New()
@ -721,7 +770,10 @@ Enjoy your time on the relay!`, monthlyPrice, monthlyPrice, string(relayNpubForC
// Add expiration tag (5 days from creation) // Add expiration tag (5 days from creation)
noteExpiry := time.Now().AddDate(0, 0, 5) noteExpiry := time.Now().AddDate(0, 0, 5)
*ev.Tags = append(*ev.Tags, tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix()))) *ev.Tags = append(
*ev.Tags,
tag.NewFromAny("expiration", fmt.Sprintf("%d", noteExpiry.Unix())),
)
// Add "private" tag with authorized npubs (user and relay) // Add "private" tag with authorized npubs (user and relay)
var authorizedNpubs []string var authorizedNpubs []string
@ -749,7 +801,7 @@ Enjoy your time on the relay!`, monthlyPrice, monthlyPrice, string(relayNpubForC
// Sign and save the event // Sign and save the event
ev.Sign(sign) ev.Sign(sign)
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil { if err, _ := pp.db.SaveEvent(pp.ctx, ev); err != nil {
return fmt.Errorf("failed to save welcome note: %w", err) return fmt.Errorf("failed to save welcome note: %w", err)
} }
@ -846,13 +898,15 @@ func (pp *PaymentProcessor) UpdateRelayProfile() error {
relayURL := strings.Replace(pp.getDashboardURL(), "https://", "wss://", 1) relayURL := strings.Replace(pp.getDashboardURL(), "https://", "wss://", 1)
// Create profile content as JSON // Create profile content as JSON
profileContent := fmt.Sprintf(`{ profileContent := fmt.Sprintf(
`{
"name": "Relay Bot", "name": "Relay Bot",
"about": "This relay requires a subscription to access. Zap any of my notes to pay for access. Monthly price: %d sats (%d sats/day). Relay: %s", "about": "This relay requires a subscription to access. Zap any of my notes to pay for access. Monthly price: %d sats (%d sats/day). Relay: %s",
"lud16": "", "lud16": "",
"nip05": "", "nip05": "",
"website": "%s" "website": "%s"
}`, monthlyPrice, dailyRate, relayURL, pp.getDashboardURL()) }`, monthlyPrice, dailyRate, relayURL, pp.getDashboardURL(),
)
// Build the profile event // Build the profile event
ev := event.New() ev := event.New()
@ -864,7 +918,7 @@ func (pp *PaymentProcessor) UpdateRelayProfile() error {
// Sign and save the event // Sign and save the event
ev.Sign(sign) ev.Sign(sign)
if _, _, err := pp.db.SaveEvent(pp.ctx, ev); err != nil { if err, _ := pp.db.SaveEvent(pp.ctx, ev); err != nil {
return fmt.Errorf("failed to save relay profile: %w", err) return fmt.Errorf("failed to save relay profile: %w", err)
} }

8
cmd/benchmark/main.go

@ -370,7 +370,7 @@ func (b *Benchmark) RunPeakThroughputTest() {
for ev := range eventChan { for ev := range eventChan {
eventStart := time.Now() eventStart := time.Now()
_, _, err := b.db.SaveEvent(ctx, ev) err, _ := b.db.SaveEvent(ctx, ev)
latency := time.Since(eventStart) latency := time.Since(eventStart)
mu.Lock() mu.Lock()
@ -460,7 +460,7 @@ func (b *Benchmark) RunBurstPatternTest() {
defer wg.Done() defer wg.Done()
eventStart := time.Now() eventStart := time.Now()
_, _, err := b.db.SaveEvent(ctx, ev) err, _ := b.db.SaveEvent(ctx, ev)
latency := time.Since(eventStart) latency := time.Since(eventStart)
mu.Lock() mu.Lock()
@ -554,7 +554,7 @@ func (b *Benchmark) RunMixedReadWriteTest() {
if eventIndex%2 == 0 { if eventIndex%2 == 0 {
// Write operation // Write operation
writeStart := time.Now() writeStart := time.Now()
_, _, err := b.db.SaveEvent(ctx, events[eventIndex]) err, _ := b.db.SaveEvent(ctx, events[eventIndex])
writeLatency := time.Since(writeStart) writeLatency := time.Since(writeStart)
mu.Lock() mu.Lock()
@ -878,7 +878,7 @@ func (b *Benchmark) RunConcurrentQueryStoreTest() {
for time.Since(start) < b.config.TestDuration && eventIndex < len(writeEvents) { for time.Since(start) < b.config.TestDuration && eventIndex < len(writeEvents) {
// Write operation // Write operation
writeStart := time.Now() writeStart := time.Now()
_, _, err := b.db.SaveEvent(ctx, writeEvents[eventIndex]) err, _ := b.db.SaveEvent(ctx, writeEvents[eventIndex])
writeLatency := time.Since(writeStart) writeLatency := time.Since(writeStart)
mu.Lock() mu.Lock()

2
pkg/acl/follows.go

@ -422,7 +422,7 @@ func (f *Follows) startSubscriptions(ctx context.Context) {
) )
} }
if _, _, err = f.D.SaveEvent( if err, _ = f.D.SaveEvent(
ctx, res.Event, ctx, res.Event,
); err != nil { ); err != nil {
if !strings.HasPrefix( if !strings.HasPrefix(

2
pkg/database/export_test.go

@ -55,7 +55,7 @@ func TestExport(t *testing.T) {
} }
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event: %v", err) t.Fatalf("Failed to save event: %v", err)
} }

2
pkg/database/fetch-event-by-serial_test.go

@ -58,7 +58,7 @@ func TestFetchEventBySerial(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/get-serial-by-id_test.go

@ -56,7 +56,7 @@ func TestGetSerialById(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/get-serials-by-range_test.go

@ -63,7 +63,7 @@ func TestGetSerialsByRange(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/import.go

@ -59,7 +59,7 @@ func (d *D) Import(rr io.Reader) {
continue continue
} }
if _, _, err = d.SaveEvent(d.ctx, ev); err != nil { if err, _ = d.SaveEvent(d.ctx, ev); err != nil {
// return the pooled buffer on error paths too // return the pooled buffer on error paths too
ev.Free() ev.Free()
continue continue

6
pkg/database/query-events-multiple-param-replaceable_test.go

@ -43,7 +43,7 @@ func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
baseEvent.Sign(sign) baseEvent.Sign(sign)
// Save the base parameterized replaceable event // Save the base parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, baseEvent); err != nil { if err, _ := db.SaveEvent(ctx, baseEvent); err != nil {
t.Fatalf("Failed to save base parameterized replaceable event: %v", err) t.Fatalf("Failed to save base parameterized replaceable event: %v", err)
} }
@ -61,7 +61,7 @@ func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
newerEvent.Sign(sign) newerEvent.Sign(sign)
// Save the newer parameterized replaceable event // Save the newer parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, newerEvent); err != nil { if err, _ := db.SaveEvent(ctx, newerEvent); err != nil {
t.Fatalf( t.Fatalf(
"Failed to save newer parameterized replaceable event: %v", err, "Failed to save newer parameterized replaceable event: %v", err,
) )
@ -81,7 +81,7 @@ func TestMultipleParameterizedReplaceableEvents(t *testing.T) {
newestEvent.Sign(sign) newestEvent.Sign(sign)
// Save the newest parameterized replaceable event // Save the newest parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, newestEvent); err != nil { if err, _ := db.SaveEvent(ctx, newestEvent); err != nil {
t.Fatalf( t.Fatalf(
"Failed to save newest parameterized replaceable event: %v", err, "Failed to save newest parameterized replaceable event: %v", err,
) )

364
pkg/database/query-events-search_test.go

@ -1,194 +1,196 @@
package database package database
import ( import (
"context" "context"
"os" "os"
"testing" "testing"
"time" "time"
"lol.mleku.dev/chk" "lol.mleku.dev/chk"
"next.orly.dev/pkg/crypto/p256k" "next.orly.dev/pkg/crypto/p256k"
"next.orly.dev/pkg/encoders/event" "next.orly.dev/pkg/encoders/event"
"next.orly.dev/pkg/encoders/filter" "next.orly.dev/pkg/encoders/filter"
"next.orly.dev/pkg/encoders/kind" "next.orly.dev/pkg/encoders/kind"
"next.orly.dev/pkg/encoders/tag" "next.orly.dev/pkg/encoders/tag"
"next.orly.dev/pkg/encoders/timestamp" "next.orly.dev/pkg/encoders/timestamp"
) )
// helper to create a fresh DB // helper to create a fresh DB
func newTestDB(t *testing.T) (*D, context.Context, context.CancelFunc, string) { func newTestDB(t *testing.T) (*D, context.Context, context.CancelFunc, string) {
t.Helper() t.Helper()
tempDir, err := os.MkdirTemp("", "search-db-*") tempDir, err := os.MkdirTemp("", "search-db-*")
if err != nil { if err != nil {
t.Fatalf("Failed to create temp dir: %v", err) t.Fatalf("Failed to create temp dir: %v", err)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
db, err := New(ctx, cancel, tempDir, "error") db, err := New(ctx, cancel, tempDir, "error")
if err != nil { if err != nil {
cancel() cancel()
os.RemoveAll(tempDir) os.RemoveAll(tempDir)
t.Fatalf("Failed to init DB: %v", err) t.Fatalf("Failed to init DB: %v", err)
} }
return db, ctx, cancel, tempDir return db, ctx, cancel, tempDir
} }
// TestQueryEventsBySearchTerms creates a small set of events with content and tags, // TestQueryEventsBySearchTerms creates a small set of events with content and tags,
// saves them, then queries using filter.Search to ensure the word index works. // saves them, then queries using filter.Search to ensure the word index works.
func TestQueryEventsBySearchTerms(t *testing.T) { func TestQueryEventsBySearchTerms(t *testing.T) {
db, ctx, cancel, tempDir := newTestDB(t) db, ctx, cancel, tempDir := newTestDB(t)
defer func() { defer func() {
// cancel context first to stop background routines cleanly // cancel context first to stop background routines cleanly
cancel() cancel()
db.Close() db.Close()
os.RemoveAll(tempDir) os.RemoveAll(tempDir)
}() }()
// signer for all events // signer for all events
sign := new(p256k.Signer) sign := new(p256k.Signer)
if err := sign.Generate(); chk.E(err) { if err := sign.Generate(); chk.E(err) {
t.Fatalf("signer generate: %v", err) t.Fatalf("signer generate: %v", err)
} }
now := timestamp.Now().V now := timestamp.Now().V
// Events to cover tokenizer rules: // Events to cover tokenizer rules:
// - regular words // - regular words
// - URLs ignored // - URLs ignored
// - 64-char hex ignored // - 64-char hex ignored
// - nostr: URIs ignored // - nostr: URIs ignored
// - #[n] mentions ignored // - #[n] mentions ignored
// - tag fields included in search // - tag fields included in search
// 1. Contains words: "alpha beta", plus URL and hex (ignored) // 1. Contains words: "alpha beta", plus URL and hex (ignored)
ev1 := event.New() ev1 := event.New()
ev1.Kind = kind.TextNote.K ev1.Kind = kind.TextNote.K
ev1.Pubkey = sign.Pub() ev1.Pubkey = sign.Pub()
ev1.CreatedAt = now - 5 ev1.CreatedAt = now - 5
ev1.Content = []byte("Alpha beta visit https://example.com deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") ev1.Content = []byte("Alpha beta visit https://example.com deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
ev1.Tags = tag.NewS() ev1.Tags = tag.NewS()
ev1.Sign(sign) ev1.Sign(sign)
if _, _, err := db.SaveEvent(ctx, ev1); err != nil { if err, _ := db.SaveEvent(ctx, ev1); err != nil {
t.Fatalf("save ev1: %v", err) t.Fatalf("save ev1: %v", err)
} }
// 2. Contains overlap word "beta" and unique "gamma" and nostr: URI ignored // 2. Contains overlap word "beta" and unique "gamma" and nostr: URI ignored
ev2 := event.New() ev2 := event.New()
ev2.Kind = kind.TextNote.K ev2.Kind = kind.TextNote.K
ev2.Pubkey = sign.Pub() ev2.Pubkey = sign.Pub()
ev2.CreatedAt = now - 4 ev2.CreatedAt = now - 4
ev2.Content = []byte("beta and GAMMA with nostr:nevent1qqqqq") ev2.Content = []byte("beta and GAMMA with nostr:nevent1qqqqq")
ev2.Tags = tag.NewS() ev2.Tags = tag.NewS()
ev2.Sign(sign) ev2.Sign(sign)
if _, _, err := db.SaveEvent(ctx, ev2); err != nil { if err, _ := db.SaveEvent(ctx, ev2); err != nil {
t.Fatalf("save ev2: %v", err) t.Fatalf("save ev2: %v", err)
} }
// 3. Contains only a URL (should not create word tokens) and mention #[1] (ignored) // 3. Contains only a URL (should not create word tokens) and mention #[1] (ignored)
ev3 := event.New() ev3 := event.New()
ev3.Kind = kind.TextNote.K ev3.Kind = kind.TextNote.K
ev3.Pubkey = sign.Pub() ev3.Pubkey = sign.Pub()
ev3.CreatedAt = now - 3 ev3.CreatedAt = now - 3
ev3.Content = []byte("see www.example.org #[1]") ev3.Content = []byte("see www.example.org #[1]")
ev3.Tags = tag.NewS() ev3.Tags = tag.NewS()
ev3.Sign(sign) ev3.Sign(sign)
if _, _, err := db.SaveEvent(ctx, ev3); err != nil { if err, _ := db.SaveEvent(ctx, ev3); err != nil {
t.Fatalf("save ev3: %v", err) t.Fatalf("save ev3: %v", err)
} }
// 4. No content words, but tag value has searchable words: "delta epsilon" // 4. No content words, but tag value has searchable words: "delta epsilon"
ev4 := event.New() ev4 := event.New()
ev4.Kind = kind.TextNote.K ev4.Kind = kind.TextNote.K
ev4.Pubkey = sign.Pub() ev4.Pubkey = sign.Pub()
ev4.CreatedAt = now - 2 ev4.CreatedAt = now - 2
ev4.Content = []byte("") ev4.Content = []byte("")
ev4.Tags = tag.NewS() ev4.Tags = tag.NewS()
*ev4.Tags = append(*ev4.Tags, tag.NewFromAny("t", "delta epsilon")) *ev4.Tags = append(*ev4.Tags, tag.NewFromAny("t", "delta epsilon"))
ev4.Sign(sign) ev4.Sign(sign)
if _, _, err := db.SaveEvent(ctx, ev4); err != nil { if err, _ := db.SaveEvent(ctx, ev4); err != nil {
t.Fatalf("save ev4: %v", err) t.Fatalf("save ev4: %v", err)
} }
// 5. Another event with both content and tag tokens for ordering checks // 5. Another event with both content and tag tokens for ordering checks
ev5 := event.New() ev5 := event.New()
ev5.Kind = kind.TextNote.K ev5.Kind = kind.TextNote.K
ev5.Pubkey = sign.Pub() ev5.Pubkey = sign.Pub()
ev5.CreatedAt = now - 1 ev5.CreatedAt = now - 1
ev5.Content = []byte("alpha DELTA mixed-case and link http://foo.bar") ev5.Content = []byte("alpha DELTA mixed-case and link http://foo.bar")
ev5.Tags = tag.NewS() ev5.Tags = tag.NewS()
*ev5.Tags = append(*ev5.Tags, tag.NewFromAny("t", "zeta")) *ev5.Tags = append(*ev5.Tags, tag.NewFromAny("t", "zeta"))
ev5.Sign(sign) ev5.Sign(sign)
if _, _, err := db.SaveEvent(ctx, ev5); err != nil { if err, _ := db.SaveEvent(ctx, ev5); err != nil {
t.Fatalf("save ev5: %v", err) t.Fatalf("save ev5: %v", err)
} }
// Small sleep to ensure created_at ordering is the only factor // Small sleep to ensure created_at ordering is the only factor
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
// Helper to run a search and return IDs // Helper to run a search and return IDs
run := func(q string) ([]*event.E, error) { run := func(q string) ([]*event.E, error) {
f := &filter.F{Search: []byte(q)} f := &filter.F{Search: []byte(q)}
return db.QueryEvents(ctx, f) return db.QueryEvents(ctx, f)
} }
// Single-term search: alpha -> should match ev1 and ev5 ordered by created_at desc (ev5 newer) // Single-term search: alpha -> should match ev1 and ev5 ordered by created_at desc (ev5 newer)
if evs, err := run("alpha"); err != nil { if evs, err := run("alpha"); err != nil {
t.Fatalf("search alpha: %v", err) t.Fatalf("search alpha: %v", err)
} else { } else {
if len(evs) != 2 { if len(evs) != 2 {
t.Fatalf("alpha expected 2 results, got %d", len(evs)) t.Fatalf("alpha expected 2 results, got %d", len(evs))
} }
if !(evs[0].CreatedAt >= evs[1].CreatedAt) { if !(evs[0].CreatedAt >= evs[1].CreatedAt) {
t.Fatalf("results not ordered by created_at desc") t.Fatalf("results not ordered by created_at desc")
} }
} }
// Overlap term beta -> ev1 and ev2 // Overlap term beta -> ev1 and ev2
if evs, err := run("beta"); err != nil { if evs, err := run("beta"); err != nil {
t.Fatalf("search beta: %v", err) t.Fatalf("search beta: %v", err)
} else if len(evs) != 2 { } else if len(evs) != 2 {
t.Fatalf("beta expected 2 results, got %d", len(evs)) t.Fatalf("beta expected 2 results, got %d", len(evs))
} }
// Unique term gamma -> only ev2 // Unique term gamma -> only ev2
if evs, err := run("gamma"); err != nil { if evs, err := run("gamma"); err != nil {
t.Fatalf("search gamma: %v", err) t.Fatalf("search gamma: %v", err)
} else if len(evs) != 1 { } else if len(evs) != 1 {
t.Fatalf("gamma expected 1 result, got %d", len(evs)) t.Fatalf("gamma expected 1 result, got %d", len(evs))
} }
// URL terms should be ignored: example -> appears only as URL in ev1/ev3/ev5; tokenizer ignores URLs so expect 0 // URL terms should be ignored: example -> appears only as URL in ev1/ev3/ev5; tokenizer ignores URLs so expect 0
if evs, err := run("example"); err != nil { if evs, err := run("example"); err != nil {
t.Fatalf("search example: %v", err) t.Fatalf("search example: %v", err)
} else if len(evs) != 0 { } else if len(evs) != 0 {
t.Fatalf("example expected 0 results (URL tokens ignored), got %d", len(evs)) t.Fatalf(
} "example expected 0 results (URL tokens ignored), got %d", len(evs),
)
// Tag words searchable: delta should match ev4 and ev5 (delta in tag for ev4, in content for ev5) }
if evs, err := run("delta"); err != nil {
t.Fatalf("search delta: %v", err) // Tag words searchable: delta should match ev4 and ev5 (delta in tag for ev4, in content for ev5)
} else if len(evs) != 2 { if evs, err := run("delta"); err != nil {
t.Fatalf("delta expected 2 results, got %d", len(evs)) t.Fatalf("search delta: %v", err)
} } else if len(evs) != 2 {
t.Fatalf("delta expected 2 results, got %d", len(evs))
// Very short token ignored: single-letter should yield 0 }
if evs, err := run("a"); err != nil {
t.Fatalf("search short token: %v", err) // Very short token ignored: single-letter should yield 0
} else if len(evs) != 0 { if evs, err := run("a"); err != nil {
t.Fatalf("single-letter expected 0 results, got %d", len(evs)) t.Fatalf("search short token: %v", err)
} } else if len(evs) != 0 {
t.Fatalf("single-letter expected 0 results, got %d", len(evs))
// 64-char hex should be ignored }
hex64 := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
if evs, err := run(hex64); err != nil { // 64-char hex should be ignored
t.Fatalf("search hex64: %v", err) hex64 := "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
} else if len(evs) != 0 { if evs, err := run(hex64); err != nil {
t.Fatalf("hex64 expected 0 results, got %d", len(evs)) t.Fatalf("search hex64: %v", err)
} } else if len(evs) != 0 {
t.Fatalf("hex64 expected 0 results, got %d", len(evs))
// nostr: scheme ignored }
if evs, err := run("nostr:nevent1qqqqq"); err != nil {
t.Fatalf("search nostr: %v", err) // nostr: scheme ignored
} else if len(evs) != 0 { if evs, err := run("nostr:nevent1qqqqq"); err != nil {
t.Fatalf("nostr: expected 0 results, got %d", len(evs)) t.Fatalf("search nostr: %v", err)
} } else if len(evs) != 0 {
t.Fatalf("nostr: expected 0 results, got %d", len(evs))
}
} }

14
pkg/database/query-events_test.go

@ -64,7 +64,7 @@ func setupTestDB(t *testing.T) (
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }
@ -204,7 +204,7 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
replaceableEvent.Tags = tag.NewS() replaceableEvent.Tags = tag.NewS()
replaceableEvent.Sign(sign) replaceableEvent.Sign(sign)
// Save the replaceable event // Save the replaceable event
if _, _, err := db.SaveEvent(ctx, replaceableEvent); err != nil { if err, _ := db.SaveEvent(ctx, replaceableEvent); err != nil {
t.Errorf("Failed to save replaceable event: %v", err) t.Errorf("Failed to save replaceable event: %v", err)
} }
@ -217,7 +217,7 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
newerEvent.Tags = tag.NewS() newerEvent.Tags = tag.NewS()
newerEvent.Sign(sign) newerEvent.Sign(sign)
// Save the newer event // Save the newer event
if _, _, err := db.SaveEvent(ctx, newerEvent); err != nil { if err, _ := db.SaveEvent(ctx, newerEvent); err != nil {
t.Errorf("Failed to save newer event: %v", err) t.Errorf("Failed to save newer event: %v", err)
} }
@ -286,7 +286,7 @@ func TestReplaceableEventsAndDeletion(t *testing.T) {
) )
// Save the deletion event // Save the deletion event
if _, _, err = db.SaveEvent(ctx, deletionEvent); err != nil { if err, _ = db.SaveEvent(ctx, deletionEvent); err != nil {
t.Fatalf("Failed to save deletion event: %v", err) t.Fatalf("Failed to save deletion event: %v", err)
} }
@ -371,7 +371,7 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
paramEvent.Sign(sign) paramEvent.Sign(sign)
// Save the parameterized replaceable event // Save the parameterized replaceable event
if _, _, err := db.SaveEvent(ctx, paramEvent); err != nil { if err, _ := db.SaveEvent(ctx, paramEvent); err != nil {
t.Fatalf("Failed to save parameterized replaceable event: %v", err) t.Fatalf("Failed to save parameterized replaceable event: %v", err)
} }
@ -397,7 +397,7 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
paramDeletionEvent.Sign(sign) paramDeletionEvent.Sign(sign)
// Save the parameterized deletion event // Save the parameterized deletion event
if _, _, err := db.SaveEvent(ctx, paramDeletionEvent); err != nil { if err, _ := db.SaveEvent(ctx, paramDeletionEvent); err != nil {
t.Fatalf("Failed to save parameterized deletion event: %v", err) t.Fatalf("Failed to save parameterized deletion event: %v", err)
} }
@ -430,7 +430,7 @@ func TestParameterizedReplaceableEventsAndDeletion(t *testing.T) {
paramDeletionEvent2.Sign(sign) paramDeletionEvent2.Sign(sign)
// Save the parameterized deletion event with e-tag // Save the parameterized deletion event with e-tag
if _, _, err := db.SaveEvent(ctx, paramDeletionEvent2); err != nil { if err, _ := db.SaveEvent(ctx, paramDeletionEvent2); err != nil {
t.Fatalf( t.Fatalf(
"Failed to save parameterized deletion event with e-tag: %v", err, "Failed to save parameterized deletion event with e-tag: %v", err,
) )

2
pkg/database/query-for-authors-tags_test.go

@ -58,7 +58,7 @@ func TestQueryForAuthorsTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-ids_test.go

@ -60,7 +60,7 @@ func TestQueryForIds(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds-authors-tags_test.go

@ -59,7 +59,7 @@ func TestQueryForKindsAuthorsTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds-authors_test.go

@ -59,7 +59,7 @@ func TestQueryForKindsAuthors(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds-tags_test.go

@ -59,7 +59,7 @@ func TestQueryForKindsTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-kinds_test.go

@ -58,7 +58,7 @@ func TestQueryForKinds(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-serials_test.go

@ -61,7 +61,7 @@ func TestQueryForSerials(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

2
pkg/database/query-for-tags_test.go

@ -58,7 +58,7 @@ func TestQueryForTags(t *testing.T) {
events = append(events, ev) events = append(events, ev)
// Save the event to the database // Save the event to the database
if _, _, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }

26
pkg/database/save-event.go

@ -9,7 +9,6 @@ import (
"github.com/dgraph-io/badger/v4" "github.com/dgraph-io/badger/v4"
"lol.mleku.dev/chk" "lol.mleku.dev/chk"
"lol.mleku.dev/log"
"next.orly.dev/pkg/database/indexes" "next.orly.dev/pkg/database/indexes"
"next.orly.dev/pkg/database/indexes/types" "next.orly.dev/pkg/database/indexes/types"
"next.orly.dev/pkg/encoders/event" "next.orly.dev/pkg/encoders/event"
@ -103,7 +102,9 @@ func (d *D) WouldReplaceEvent(ev *event.E) (bool, types.Uint40s, error) {
} }
// SaveEvent saves an event to the database, generating all the necessary indexes. // SaveEvent saves an event to the database, generating all the necessary indexes.
func (d *D) SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) { func (d *D) SaveEvent(c context.Context, ev *event.E) (
err error, replaced bool,
) {
if ev == nil { if ev == nil {
err = errors.New("nil event") err = errors.New("nil event")
return return
@ -136,10 +137,9 @@ func (d *D) SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) {
} }
// check for replacement (separated check vs deletion) // check for replacement (separated check vs deletion)
if kind.IsReplaceable(ev.Kind) || kind.IsParameterizedReplaceable(ev.Kind) { if kind.IsReplaceable(ev.Kind) || kind.IsParameterizedReplaceable(ev.Kind) {
var wouldReplace bool
var sers types.Uint40s var sers types.Uint40s
var werr error var werr error
if wouldReplace, sers, werr = d.WouldReplaceEvent(ev); werr != nil { if replaced, sers, werr = d.WouldReplaceEvent(ev); werr != nil {
if errors.Is(werr, ErrOlderThanExisting) { if errors.Is(werr, ErrOlderThanExisting) {
if kind.IsReplaceable(ev.Kind) { if kind.IsReplaceable(ev.Kind) {
err = errors.New("blocked: event is older than existing replaceable event") err = errors.New("blocked: event is older than existing replaceable event")
@ -156,7 +156,7 @@ func (d *D) SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) {
// any other error // any other error
return return
} }
if wouldReplace { if replaced {
for _, s := range sers { for _, s := range sers {
var oldEv *event.E var oldEv *event.E
if oldEv, err = d.FetchEventBySerial(s); chk.E(err) { if oldEv, err = d.FetchEventBySerial(s); chk.E(err) {
@ -178,10 +178,6 @@ func (d *D) SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) {
if idxs, err = GetIndexesForEvent(ev, serial); chk.E(err) { if idxs, err = GetIndexesForEvent(ev, serial); chk.E(err) {
return return
} }
// log.I.S(idxs)
for _, k := range idxs {
kc += len(k)
}
// Start a transaction to save the event and all its indexes // Start a transaction to save the event and all its indexes
err = d.Update( err = d.Update(
func(txn *badger.Txn) (err error) { func(txn *badger.Txn) (err error) {
@ -209,23 +205,11 @@ func (d *D) SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) {
v := new(bytes.Buffer) v := new(bytes.Buffer)
ev.MarshalBinary(v) ev.MarshalBinary(v)
kb, vb := k.Bytes(), v.Bytes() kb, vb := k.Bytes(), v.Bytes()
kc += len(kb)
vc += len(vb)
// log.I.S(kb, vb)
if err = txn.Set(kb, vb); chk.E(err) { if err = txn.Set(kb, vb); chk.E(err) {
return return
} }
return return
}, },
) )
log.T.F(
"total data written: %d bytes keys %d bytes values for event ID %s", kc,
vc, hex.Enc(ev.ID),
)
// log.T.C(
// func() string {
// return fmt.Sprintf("event:\n%s\n", ev.Serialize())
// },
// )
return return
} }

10
pkg/database/save-event_test.go

@ -65,7 +65,7 @@ func TestSaveEvents(t *testing.T) {
// Save the event to the database // Save the event to the database
var k, v int var k, v int
if k, v, err = db.SaveEvent(ctx, ev); err != nil { if err, _ = db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event #%d: %v", eventCount+1, err) t.Fatalf("Failed to save event #%d: %v", eventCount+1, err)
} }
kc += k kc += k
@ -125,7 +125,7 @@ func TestDeletionEventWithETagRejection(t *testing.T) {
regularEvent.Sign(sign) regularEvent.Sign(sign)
// Save the regular event // Save the regular event
if _, _, err := db.SaveEvent(ctx, regularEvent); err != nil { if err, _ := db.SaveEvent(ctx, regularEvent); err != nil {
t.Fatalf("Failed to save regular event: %v", err) t.Fatalf("Failed to save regular event: %v", err)
} }
@ -151,7 +151,7 @@ func TestDeletionEventWithETagRejection(t *testing.T) {
err = errorf.E("deletion events referencing other events with 'e' tag are not allowed") err = errorf.E("deletion events referencing other events with 'e' tag are not allowed")
} else { } else {
// Try to save the deletion event // Try to save the deletion event
_, _, err = db.SaveEvent(ctx, deletionEvent) err, _ = db.SaveEvent(ctx, deletionEvent)
} }
if err == nil { if err == nil {
@ -204,12 +204,12 @@ func TestSaveExistingEvent(t *testing.T) {
ev.Sign(sign) ev.Sign(sign)
// Save the event for the first time // Save the event for the first time
if _, _, err := db.SaveEvent(ctx, ev); err != nil { if err, _ := db.SaveEvent(ctx, ev); err != nil {
t.Fatalf("Failed to save event: %v", err) t.Fatalf("Failed to save event: %v", err)
} }
// Try to save the same event again, it should be rejected // Try to save the same event again, it should be rejected
_, _, err = db.SaveEvent(ctx, ev) err, _ = db.SaveEvent(ctx, ev)
if err == nil { if err == nil {
t.Fatal("Expected error when saving an existing event, but got nil") t.Fatal("Expected error when saving an existing event, but got nil")
} }

2
pkg/interfaces/store/store_interface.go

@ -84,7 +84,7 @@ type Saver interface {
// SaveEvent is called once relay.AcceptEvent reports true. The owners // SaveEvent is called once relay.AcceptEvent reports true. The owners
// parameter is for designating admins whose delete by e tag events apply // parameter is for designating admins whose delete by e tag events apply
// the same as author's own. // the same as author's own.
SaveEvent(c context.Context, ev *event.E) (kc, vc int, err error) SaveEvent(c context.Context, ev *event.E) (err error, replaced bool)
} }
type Importer interface { type Importer interface {

36
pkg/spider/spider.go

@ -293,7 +293,10 @@ func (s *Spider) calculateOptimalChunkSize() int {
chunkSize = 10 chunkSize = 10
} }
log.D.F("Spider: calculated optimal chunk size: %d pubkeys (max would be %d)", chunkSize, maxPubkeys) log.D.F(
"Spider: calculated optimal chunk size: %d pubkeys (max would be %d)",
chunkSize, maxPubkeys,
)
return chunkSize return chunkSize
} }
@ -301,7 +304,10 @@ func (s *Spider) calculateOptimalChunkSize() int {
func (s *Spider) queryRelayForEvents( func (s *Spider) queryRelayForEvents(
relayURL string, followedPubkeys [][]byte, startTime, endTime time.Time, relayURL string, followedPubkeys [][]byte, startTime, endTime time.Time,
) (int, error) { ) (int, error) {
log.T.F("Spider sync: querying relay %s with %d pubkeys", relayURL, len(followedPubkeys)) log.T.F(
"Spider sync: querying relay %s with %d pubkeys", relayURL,
len(followedPubkeys),
)
// Connect to the relay with a timeout context // Connect to the relay with a timeout context
ctx, cancel := context.WithTimeout(s.ctx, 30*time.Second) ctx, cancel := context.WithTimeout(s.ctx, 30*time.Second)
@ -324,8 +330,10 @@ func (s *Spider) queryRelayForEvents(
} }
chunk := followedPubkeys[i:end] chunk := followedPubkeys[i:end]
log.T.F("Spider sync: processing chunk %d-%d (%d pubkeys) for relay %s", log.T.F(
i, end-1, len(chunk), relayURL) "Spider sync: processing chunk %d-%d (%d pubkeys) for relay %s",
i, end-1, len(chunk), relayURL,
)
// Create filter for this chunk of pubkeys // Create filter for this chunk of pubkeys
f := &filter.F{ f := &filter.F{
@ -338,8 +346,10 @@ func (s *Spider) queryRelayForEvents(
// Subscribe to get events for this chunk // Subscribe to get events for this chunk
sub, err := client.Subscribe(ctx, filter.NewS(f)) sub, err := client.Subscribe(ctx, filter.NewS(f))
if err != nil { if err != nil {
log.E.F("Spider sync: failed to subscribe to chunk %d-%d for relay %s: %v", log.E.F(
i, end-1, relayURL, err) "Spider sync: failed to subscribe to chunk %d-%d for relay %s: %v",
i, end-1, relayURL, err,
)
continue continue
} }
@ -385,7 +395,7 @@ func (s *Spider) queryRelayForEvents(
} }
// Save the event to the database // Save the event to the database
if _, _, err := s.db.SaveEvent(s.ctx, ev); err != nil { if err, _ := s.db.SaveEvent(s.ctx, ev); err != nil {
if !strings.HasPrefix(err.Error(), "blocked:") { if !strings.HasPrefix(err.Error(), "blocked:") {
log.T.F( log.T.F(
"Spider sync: error saving event from relay %s: %v", "Spider sync: error saving event from relay %s: %v",
@ -410,12 +420,16 @@ func (s *Spider) queryRelayForEvents(
sub.Unsub() sub.Unsub()
totalEventsSaved += chunkEventsSaved totalEventsSaved += chunkEventsSaved
log.T.F("Spider sync: completed chunk %d-%d for relay %s, saved %d events", log.T.F(
i, end-1, relayURL, chunkEventsSaved) "Spider sync: completed chunk %d-%d for relay %s, saved %d events",
i, end-1, relayURL, chunkEventsSaved,
)
} }
log.T.F("Spider sync: completed all chunks for relay %s, total saved %d events", log.T.F(
relayURL, totalEventsSaved) "Spider sync: completed all chunks for relay %s, total saved %d events",
relayURL, totalEventsSaved,
)
return totalEventsSaved, nil return totalEventsSaved, nil
} }

2
pkg/version/version

@ -1 +1 @@
v0.12.3 v0.14.0
Loading…
Cancel
Save