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