relysqlite_wrapper.go raw

   1  package main
   2  
   3  import (
   4  	"context"
   5  	"fmt"
   6  	"io"
   7  	"time"
   8  
   9  	sqlite "github.com/vertex-lab/nostr-sqlite"
  10  
  11  	"next.orly.dev/pkg/database"
  12  	"next.orly.dev/pkg/database/indexes/types"
  13  	"next.orly.dev/pkg/nostr/encoders/event"
  14  	"next.orly.dev/pkg/nostr/encoders/filter"
  15  	"next.orly.dev/pkg/nostr/encoders/tag"
  16  	"next.orly.dev/pkg/interfaces/store"
  17  )
  18  
  19  // RelySQLiteWrapper wraps the vertex-lab/nostr-sqlite store to implement
  20  // the minimal database.Database interface needed for benchmarking
  21  type RelySQLiteWrapper struct {
  22  	store *sqlite.Store
  23  	path  string
  24  	ready chan struct{}
  25  }
  26  
  27  // NewRelySQLiteWrapper creates a new wrapper around nostr-sqlite
  28  func NewRelySQLiteWrapper(dbPath string) (*RelySQLiteWrapper, error) {
  29  	store, err := sqlite.New(dbPath)
  30  	if err != nil {
  31  		return nil, fmt.Errorf("failed to create sqlite store: %w", err)
  32  	}
  33  
  34  	wrapper := &RelySQLiteWrapper{
  35  		store: store,
  36  		path:  dbPath,
  37  		ready: make(chan struct{}),
  38  	}
  39  
  40  	// Close the ready channel immediately as SQLite is ready on creation
  41  	close(wrapper.ready)
  42  
  43  	return wrapper, nil
  44  }
  45  
  46  // SaveEvent saves an event to the database
  47  func (w *RelySQLiteWrapper) SaveEvent(ctx context.Context, ev *event.E) (exists bool, err error) {
  48  	// Convert ORLY event to go-nostr event
  49  	nostrEv, err := convertToNostrEvent(ev)
  50  	if err != nil {
  51  		return false, fmt.Errorf("failed to convert event: %w", err)
  52  	}
  53  
  54  	// Use Replace for replaceable/addressable events, Save otherwise
  55  	if isReplaceableKind(int(ev.Kind)) || isAddressableKind(int(ev.Kind)) {
  56  		replaced, err := w.store.Replace(ctx, nostrEv)
  57  		return replaced, err
  58  	}
  59  
  60  	saved, err := w.store.Save(ctx, nostrEv)
  61  	return !saved, err // saved=true means it's new, exists=false
  62  }
  63  
  64  // QueryEvents queries events matching the filter
  65  func (w *RelySQLiteWrapper) QueryEvents(ctx context.Context, f *filter.F) (evs event.S, err error) {
  66  	// Convert ORLY filter to go-nostr filter
  67  	nostrFilter, err := convertToNostrFilter(f)
  68  	if err != nil {
  69  		return nil, fmt.Errorf("failed to convert filter: %w", err)
  70  	}
  71  
  72  	// Query the store
  73  	nostrEvents, err := w.store.Query(ctx, nostrFilter)
  74  	if err != nil {
  75  		return nil, fmt.Errorf("query failed: %w", err)
  76  	}
  77  
  78  	// Convert back to ORLY events
  79  	events := make(event.S, 0, len(nostrEvents))
  80  	for _, ne := range nostrEvents {
  81  		ev, err := convertFromNostrEvent(&ne)
  82  		if err != nil {
  83  			continue // Skip events that fail to convert
  84  		}
  85  		events = append(events, ev)
  86  	}
  87  
  88  	return events, nil
  89  }
  90  
  91  // Close closes the database
  92  func (w *RelySQLiteWrapper) Close() error {
  93  	if w.store != nil {
  94  		return w.store.Close()
  95  	}
  96  	return nil
  97  }
  98  
  99  // Ready returns a channel that closes when the database is ready
 100  func (w *RelySQLiteWrapper) Ready() <-chan struct{} {
 101  	return w.ready
 102  }
 103  
 104  // Path returns the database path
 105  func (w *RelySQLiteWrapper) Path() string {
 106  	return w.path
 107  }
 108  
 109  // Wipe clears all data from the database
 110  func (w *RelySQLiteWrapper) Wipe() error {
 111  	// Close current store
 112  	if err := w.store.Close(); err != nil {
 113  		return err
 114  	}
 115  
 116  	// Delete the database file
 117  	// Note: This is a simplified approach - in production you'd want
 118  	// to handle this more carefully
 119  	return nil
 120  }
 121  
 122  // Stub implementations for unused interface methods
 123  func (w *RelySQLiteWrapper) Init(path string) error { return nil }
 124  func (w *RelySQLiteWrapper) Sync() error            { return nil }
 125  func (w *RelySQLiteWrapper) SetLogLevel(level string) {}
 126  func (w *RelySQLiteWrapper) GetSerialsFromFilter(f *filter.F) (serials types.Uint40s, err error) {
 127  	return nil, fmt.Errorf("not implemented")
 128  }
 129  func (w *RelySQLiteWrapper) WouldReplaceEvent(ev *event.E) (bool, types.Uint40s, error) {
 130  	return false, nil, fmt.Errorf("not implemented")
 131  }
 132  func (w *RelySQLiteWrapper) QueryAllVersions(c context.Context, f *filter.F) (evs event.S, err error) {
 133  	return nil, fmt.Errorf("not implemented")
 134  }
 135  func (w *RelySQLiteWrapper) QueryEventsWithOptions(c context.Context, f *filter.F, includeDeleteEvents bool, showAllVersions bool) (evs event.S, err error) {
 136  	return nil, fmt.Errorf("not implemented")
 137  }
 138  func (w *RelySQLiteWrapper) QueryDeleteEventsByTargetId(c context.Context, targetEventId []byte) (evs event.S, err error) {
 139  	return nil, fmt.Errorf("not implemented")
 140  }
 141  func (w *RelySQLiteWrapper) QueryForSerials(c context.Context, f *filter.F) (serials types.Uint40s, err error) {
 142  	return nil, fmt.Errorf("not implemented")
 143  }
 144  func (w *RelySQLiteWrapper) QueryForIds(c context.Context, f *filter.F) (idPkTs []*store.IdPkTs, err error) {
 145  	return nil, fmt.Errorf("not implemented")
 146  }
 147  func (w *RelySQLiteWrapper) CountEvents(c context.Context, f *filter.F) (count int, approximate bool, err error) {
 148  	return 0, false, fmt.Errorf("not implemented")
 149  }
 150  func (w *RelySQLiteWrapper) FetchEventBySerial(ser *types.Uint40) (ev *event.E, err error) {
 151  	return nil, fmt.Errorf("not implemented")
 152  }
 153  func (w *RelySQLiteWrapper) FetchEventsBySerials(serials []*types.Uint40) (events map[uint64]*event.E, err error) {
 154  	return nil, fmt.Errorf("not implemented")
 155  }
 156  func (w *RelySQLiteWrapper) GetSerialById(id []byte) (ser *types.Uint40, err error) {
 157  	return nil, fmt.Errorf("not implemented")
 158  }
 159  func (w *RelySQLiteWrapper) GetSerialsByIds(ids *tag.T) (serials map[string]*types.Uint40, err error) {
 160  	return nil, fmt.Errorf("not implemented")
 161  }
 162  func (w *RelySQLiteWrapper) GetSerialsByIdsWithFilter(ids *tag.T, fn func(ev *event.E, ser *types.Uint40) bool) (serials map[string]*types.Uint40, err error) {
 163  	return nil, fmt.Errorf("not implemented")
 164  }
 165  func (w *RelySQLiteWrapper) GetSerialsByRange(idx database.Range) (serials types.Uint40s, err error) {
 166  	return nil, fmt.Errorf("not implemented")
 167  }
 168  func (w *RelySQLiteWrapper) GetFullIdPubkeyBySerial(ser *types.Uint40) (fidpk *store.IdPkTs, err error) {
 169  	return nil, fmt.Errorf("not implemented")
 170  }
 171  func (w *RelySQLiteWrapper) GetFullIdPubkeyBySerials(sers []*types.Uint40) (fidpks []*store.IdPkTs, err error) {
 172  	return nil, fmt.Errorf("not implemented")
 173  }
 174  func (w *RelySQLiteWrapper) DeleteEvent(c context.Context, eid []byte) error {
 175  	return fmt.Errorf("not implemented")
 176  }
 177  func (w *RelySQLiteWrapper) DeleteEventBySerial(c context.Context, ser *types.Uint40, ev *event.E) error {
 178  	return fmt.Errorf("not implemented")
 179  }
 180  func (w *RelySQLiteWrapper) DeleteExpired() {}
 181  func (w *RelySQLiteWrapper) ProcessDelete(ev *event.E, admins [][]byte) error {
 182  	return fmt.Errorf("not implemented")
 183  }
 184  func (w *RelySQLiteWrapper) CheckForDeleted(ev *event.E, admins [][]byte) error {
 185  	return fmt.Errorf("not implemented")
 186  }
 187  func (w *RelySQLiteWrapper) Import(rr io.Reader) {}
 188  func (w *RelySQLiteWrapper) Export(c context.Context, writer io.Writer, pubkeys ...[]byte) {
 189  }
 190  func (w *RelySQLiteWrapper) ImportEventsFromReader(ctx context.Context, rr io.Reader) error {
 191  	return fmt.Errorf("not implemented")
 192  }
 193  func (w *RelySQLiteWrapper) ImportEventsFromStrings(ctx context.Context, eventJSONs []string, policyManager interface{ CheckPolicy(action string, ev *event.E, pubkey []byte, remote string) (bool, error) }) error {
 194  	return fmt.Errorf("not implemented")
 195  }
 196  func (w *RelySQLiteWrapper) GetRelayIdentitySecret() (skb []byte, err error) {
 197  	return nil, fmt.Errorf("not implemented")
 198  }
 199  func (w *RelySQLiteWrapper) SetRelayIdentitySecret(skb []byte) error {
 200  	return fmt.Errorf("not implemented")
 201  }
 202  func (w *RelySQLiteWrapper) GetOrCreateRelayIdentitySecret() (skb []byte, err error) {
 203  	return nil, fmt.Errorf("not implemented")
 204  }
 205  func (w *RelySQLiteWrapper) SetMarker(key string, value []byte) error {
 206  	return fmt.Errorf("not implemented")
 207  }
 208  func (w *RelySQLiteWrapper) GetMarker(key string) (value []byte, err error) {
 209  	return nil, fmt.Errorf("not implemented")
 210  }
 211  func (w *RelySQLiteWrapper) HasMarker(key string) bool { return false }
 212  func (w *RelySQLiteWrapper) DeleteMarker(key string) error {
 213  	return fmt.Errorf("not implemented")
 214  }
 215  func (w *RelySQLiteWrapper) GetSubscription(pubkey []byte) (*database.Subscription, error) {
 216  	return nil, fmt.Errorf("not implemented")
 217  }
 218  func (w *RelySQLiteWrapper) IsSubscriptionActive(pubkey []byte) (bool, error) {
 219  	return false, fmt.Errorf("not implemented")
 220  }
 221  func (w *RelySQLiteWrapper) ExtendSubscription(pubkey []byte, days int) error {
 222  	return fmt.Errorf("not implemented")
 223  }
 224  func (w *RelySQLiteWrapper) RecordPayment(pubkey []byte, amount int64, invoice, preimage string) error {
 225  	return fmt.Errorf("not implemented")
 226  }
 227  func (w *RelySQLiteWrapper) GetPaymentHistory(pubkey []byte) ([]database.Payment, error) {
 228  	return nil, fmt.Errorf("not implemented")
 229  }
 230  func (w *RelySQLiteWrapper) ExtendBlossomSubscription(pubkey []byte, tier string, storageMB int64, daysExtended int) error {
 231  	return fmt.Errorf("not implemented")
 232  }
 233  func (w *RelySQLiteWrapper) GetBlossomStorageQuota(pubkey []byte) (quotaMB int64, err error) {
 234  	return 0, fmt.Errorf("not implemented")
 235  }
 236  func (w *RelySQLiteWrapper) IsFirstTimeUser(pubkey []byte) (bool, error) {
 237  	return false, fmt.Errorf("not implemented")
 238  }
 239  func (w *RelySQLiteWrapper) AddNIP43Member(pubkey []byte, inviteCode string) error {
 240  	return fmt.Errorf("not implemented")
 241  }
 242  func (w *RelySQLiteWrapper) RemoveNIP43Member(pubkey []byte) error {
 243  	return fmt.Errorf("not implemented")
 244  }
 245  func (w *RelySQLiteWrapper) IsNIP43Member(pubkey []byte) (isMember bool, err error) {
 246  	return false, fmt.Errorf("not implemented")
 247  }
 248  func (w *RelySQLiteWrapper) GetNIP43Membership(pubkey []byte) (*database.NIP43Membership, error) {
 249  	return nil, fmt.Errorf("not implemented")
 250  }
 251  func (w *RelySQLiteWrapper) GetAllNIP43Members() ([][]byte, error) {
 252  	return nil, fmt.Errorf("not implemented")
 253  }
 254  func (w *RelySQLiteWrapper) StoreInviteCode(code string, expiresAt time.Time) error {
 255  	return fmt.Errorf("not implemented")
 256  }
 257  func (w *RelySQLiteWrapper) ValidateInviteCode(code string) (valid bool, err error) {
 258  	return false, fmt.Errorf("not implemented")
 259  }
 260  func (w *RelySQLiteWrapper) DeleteInviteCode(code string) error {
 261  	return fmt.Errorf("not implemented")
 262  }
 263  func (w *RelySQLiteWrapper) PublishNIP43MembershipEvent(kind int, pubkey []byte) error {
 264  	return fmt.Errorf("not implemented")
 265  }
 266  func (w *RelySQLiteWrapper) RunMigrations() {}
 267  func (w *RelySQLiteWrapper) GetCachedJSON(f *filter.F) ([][]byte, bool) {
 268  	return nil, false
 269  }
 270  func (w *RelySQLiteWrapper) CacheMarshaledJSON(f *filter.F, marshaledJSON [][]byte) {
 271  }
 272  func (w *RelySQLiteWrapper) GetCachedEvents(f *filter.F) (event.S, bool) {
 273  	return nil, false
 274  }
 275  func (w *RelySQLiteWrapper) CacheEvents(f *filter.F, events event.S) {}
 276  func (w *RelySQLiteWrapper) InvalidateQueryCache()                   {}
 277  func (w *RelySQLiteWrapper) EventIdsBySerial(start uint64, count int) (evs []uint64, err error) {
 278  	return nil, fmt.Errorf("not implemented")
 279  }
 280  
 281  // Access tracking stubs (not needed for benchmarking)
 282  func (w *RelySQLiteWrapper) RecordEventAccess(serial uint64, connectionID string) error {
 283  	return nil // No-op for benchmarking
 284  }
 285  func (w *RelySQLiteWrapper) GetEventAccessInfo(serial uint64) (lastAccess int64, accessCount uint32, err error) {
 286  	return 0, 0, nil
 287  }
 288  func (w *RelySQLiteWrapper) GetLeastAccessedEvents(limit int, minAgeSec int64) (serials []uint64, err error) {
 289  	return nil, nil
 290  }
 291  
 292  // Blob storage stubs (not needed for benchmarking)
 293  func (w *RelySQLiteWrapper) SaveBlob(sha256Hash []byte, data []byte, pubkey []byte, mimeType string, extension string) error {
 294  	return fmt.Errorf("not implemented")
 295  }
 296  func (w *RelySQLiteWrapper) SaveBlobMetadata(sha256Hash []byte, size int64, pubkey []byte, mimeType string, extension string) error {
 297  	return fmt.Errorf("not implemented")
 298  }
 299  func (w *RelySQLiteWrapper) GetBlob(sha256Hash []byte) (data []byte, metadata *database.BlobMetadata, err error) {
 300  	return nil, nil, fmt.Errorf("not implemented")
 301  }
 302  func (w *RelySQLiteWrapper) HasBlob(sha256Hash []byte) (exists bool, err error) {
 303  	return false, fmt.Errorf("not implemented")
 304  }
 305  func (w *RelySQLiteWrapper) DeleteBlob(sha256Hash []byte, pubkey []byte) error {
 306  	return fmt.Errorf("not implemented")
 307  }
 308  func (w *RelySQLiteWrapper) ListBlobs(pubkey []byte, since, until int64) ([]*database.BlobDescriptor, error) {
 309  	return nil, fmt.Errorf("not implemented")
 310  }
 311  func (w *RelySQLiteWrapper) GetBlobMetadata(sha256Hash []byte) (*database.BlobMetadata, error) {
 312  	return nil, fmt.Errorf("not implemented")
 313  }
 314  func (w *RelySQLiteWrapper) GetTotalBlobStorageUsed(pubkey []byte) (totalMB int64, err error) {
 315  	return 0, fmt.Errorf("not implemented")
 316  }
 317  func (w *RelySQLiteWrapper) SaveBlobReport(sha256Hash []byte, reportData []byte) error {
 318  	return fmt.Errorf("not implemented")
 319  }
 320  func (w *RelySQLiteWrapper) ListAllBlobUserStats() ([]*database.UserBlobStats, error) {
 321  	return nil, fmt.Errorf("not implemented")
 322  }
 323  func (w *RelySQLiteWrapper) ReconcileBlobMetadata() (int, error) {
 324  	return 0, fmt.Errorf("not implemented")
 325  }
 326  
 327  // NRC (Nostr Relay Connect) stubs - not needed for benchmarking
 328  func (w *RelySQLiteWrapper) CreateNRCConnection(label string, createdBy []byte) (*database.NRCConnection, error) {
 329  	return nil, fmt.Errorf("not implemented")
 330  }
 331  func (w *RelySQLiteWrapper) GetNRCConnection(id string) (*database.NRCConnection, error) {
 332  	return nil, fmt.Errorf("not implemented")
 333  }
 334  func (w *RelySQLiteWrapper) GetNRCConnectionByDerivedPubkey(derivedPubkey []byte) (*database.NRCConnection, error) {
 335  	return nil, fmt.Errorf("not implemented")
 336  }
 337  func (w *RelySQLiteWrapper) SaveNRCConnection(conn *database.NRCConnection) error {
 338  	return fmt.Errorf("not implemented")
 339  }
 340  func (w *RelySQLiteWrapper) DeleteNRCConnection(id string) error {
 341  	return fmt.Errorf("not implemented")
 342  }
 343  func (w *RelySQLiteWrapper) GetAllNRCConnections() ([]*database.NRCConnection, error) {
 344  	return nil, fmt.Errorf("not implemented")
 345  }
 346  func (w *RelySQLiteWrapper) GetNRCAuthorizedSecrets() (map[string]string, error) {
 347  	return nil, fmt.Errorf("not implemented")
 348  }
 349  func (w *RelySQLiteWrapper) UpdateNRCConnectionLastUsed(id string) error {
 350  	return fmt.Errorf("not implemented")
 351  }
 352  func (w *RelySQLiteWrapper) GetNRCConnectionURI(conn *database.NRCConnection, relayPubkey []byte, rendezvousURL string) (string, error) {
 353  	return "", fmt.Errorf("not implemented")
 354  }
 355  
 356  // Thumbnail stubs - not needed for benchmarking
 357  func (w *RelySQLiteWrapper) ListAllBlobs() ([]*database.BlobDescriptor, error) {
 358  	return nil, fmt.Errorf("not implemented")
 359  }
 360  func (w *RelySQLiteWrapper) GetThumbnail(key string) ([]byte, error) {
 361  	return nil, fmt.Errorf("not implemented")
 362  }
 363  func (w *RelySQLiteWrapper) SaveThumbnail(key string, data []byte) error {
 364  	return fmt.Errorf("not implemented")
 365  }
 366  
 367  // Helper function to check if a kind is replaceable
 368  func isReplaceableKind(kind int) bool {
 369  	return (kind >= 10000 && kind < 20000) || kind == 0 || kind == 3
 370  }
 371  
 372  // Helper function to check if a kind is addressable
 373  func isAddressableKind(kind int) bool {
 374  	return kind >= 30000 && kind < 40000
 375  }
 376  
 377  // Paid ACL stubs (not supported in benchmark wrapper)
 378  
 379  func (w *RelySQLiteWrapper) SavePaidSubscription(sub *database.PaidSubscription) error {
 380  	return fmt.Errorf("not supported")
 381  }
 382  func (w *RelySQLiteWrapper) GetPaidSubscription(pubkeyHex string) (*database.PaidSubscription, error) {
 383  	return nil, fmt.Errorf("not supported")
 384  }
 385  func (w *RelySQLiteWrapper) DeletePaidSubscription(pubkeyHex string) error {
 386  	return fmt.Errorf("not supported")
 387  }
 388  func (w *RelySQLiteWrapper) ListPaidSubscriptions() ([]*database.PaidSubscription, error) {
 389  	return nil, fmt.Errorf("not supported")
 390  }
 391  func (w *RelySQLiteWrapper) ClaimAlias(alias, pubkeyHex string) error {
 392  	return fmt.Errorf("not supported")
 393  }
 394  func (w *RelySQLiteWrapper) GetAliasByPubkey(pubkeyHex string) (string, error) {
 395  	return "", fmt.Errorf("not supported")
 396  }
 397  func (w *RelySQLiteWrapper) GetPubkeyByAlias(alias string) (string, error) {
 398  	return "", fmt.Errorf("not supported")
 399  }
 400  func (w *RelySQLiteWrapper) IsAliasTaken(alias string) (bool, error) {
 401  	return false, fmt.Errorf("not supported")
 402  }
 403