factory.go raw
1 //go:build !(js && wasm)
2
3 package database
4
5 import (
6 "context"
7 "fmt"
8 "strings"
9 "time"
10 )
11
12 // DatabaseConfig holds all database configuration options that can be passed
13 // to any database backend. Each backend uses the relevant fields for its type.
14 // This centralizes configuration instead of having each backend read env vars directly.
15 type DatabaseConfig struct {
16 // Common settings for all backends
17 DataDir string
18 LogLevel string
19
20 // Badger-specific settings
21 BlockCacheMB int // ORLY_DB_BLOCK_CACHE_MB
22 IndexCacheMB int // ORLY_DB_INDEX_CACHE_MB
23 QueryCacheDisabled bool // ORLY_QUERY_CACHE_DISABLED - disable query cache to reduce memory usage
24 QueryCacheSizeMB int // ORLY_QUERY_CACHE_SIZE_MB
25 QueryCacheMaxAge time.Duration // ORLY_QUERY_CACHE_MAX_AGE
26
27 // Serial cache settings for compact event storage
28 SerialCachePubkeys int // ORLY_SERIAL_CACHE_PUBKEYS - max pubkeys to cache (default: 100000)
29 SerialCacheEventIds int // ORLY_SERIAL_CACHE_EVENT_IDS - max event IDs to cache (default: 500000)
30
31 // Compression settings
32 ZSTDLevel int // ORLY_DB_ZSTD_LEVEL - ZSTD compression level (0=none, 1=fast, 3=default, 9=best)
33
34 // Neo4j-specific settings
35 Neo4jURI string // ORLY_NEO4J_URI
36 Neo4jUser string // ORLY_NEO4J_USER
37 Neo4jPassword string // ORLY_NEO4J_PASSWORD
38
39 // Neo4j driver tuning (memory and connection management)
40 Neo4jMaxConnPoolSize int // ORLY_NEO4J_MAX_CONN_POOL - max connection pool size (default: 25)
41 Neo4jFetchSize int // ORLY_NEO4J_FETCH_SIZE - max records per fetch batch (default: 1000)
42 Neo4jMaxTxRetrySeconds int // ORLY_NEO4J_MAX_TX_RETRY_SEC - max transaction retry time (default: 30)
43 Neo4jQueryResultLimit int // ORLY_NEO4J_QUERY_RESULT_LIMIT - max results per query (default: 10000, 0=unlimited)
44
45 // gRPC client settings (for remote database)
46 GRPCServerAddress string // ORLY_GRPC_SERVER - address of remote gRPC database server
47 GRPCConnectTimeout time.Duration // ORLY_GRPC_CONNECT_TIMEOUT - connection timeout (default: 10s)
48 }
49
50 // NewDatabase creates a database instance based on the specified type.
51 // Supported types: "badger", "neo4j"
52 func NewDatabase(
53 ctx context.Context,
54 cancel context.CancelFunc,
55 dbType string,
56 dataDir string,
57 logLevel string,
58 ) (Database, error) {
59 // Create a default config for backward compatibility with existing callers
60 cfg := &DatabaseConfig{
61 DataDir: dataDir,
62 LogLevel: logLevel,
63 }
64 return NewDatabaseWithConfig(ctx, cancel, dbType, cfg)
65 }
66
67 // NewDatabaseWithConfig creates a database instance with full configuration.
68 // This is the preferred method when you have access to the app config.
69 func NewDatabaseWithConfig(
70 ctx context.Context,
71 cancel context.CancelFunc,
72 dbType string,
73 cfg *DatabaseConfig,
74 ) (Database, error) {
75 switch strings.ToLower(dbType) {
76 case "badger", "":
77 // Use the existing badger implementation
78 return NewWithConfig(ctx, cancel, cfg)
79 case "neo4j":
80 // Use the neo4j implementation
81 if newNeo4jDatabase == nil {
82 return nil, fmt.Errorf("neo4j database backend not available (import _ \"next.orly.dev/pkg/neo4j\")")
83 }
84 return newNeo4jDatabase(ctx, cancel, cfg)
85 case "wasmdb", "indexeddb", "wasm":
86 // Use the wasmdb implementation (IndexedDB backend for WebAssembly)
87 if newWasmDBDatabase == nil {
88 return nil, fmt.Errorf("wasmdb database backend not available (import _ \"next.orly.dev/pkg/wasmdb\")")
89 }
90 return newWasmDBDatabase(ctx, cancel, cfg)
91 case "grpc":
92 // Use the gRPC client to connect to a remote database server
93 if newGRPCDatabase == nil {
94 return nil, fmt.Errorf("grpc database backend not available (import _ \"next.orly.dev/pkg/database/grpc\")")
95 }
96 return newGRPCDatabase(ctx, cancel, cfg)
97 default:
98 return nil, fmt.Errorf("unsupported database type: %s (supported: badger, neo4j, wasmdb, grpc)", dbType)
99 }
100 }
101
102 // newNeo4jDatabase creates a neo4j database instance
103 // This is defined here to avoid import cycles
104 var newNeo4jDatabase func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)
105
106 // RegisterNeo4jFactory registers the neo4j database factory
107 // This is called from the neo4j package's init() function
108 func RegisterNeo4jFactory(factory func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)) {
109 newNeo4jDatabase = factory
110 }
111
112 // newWasmDBDatabase creates a wasmdb database instance (IndexedDB backend for WebAssembly)
113 // This is defined here to avoid import cycles
114 var newWasmDBDatabase func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)
115
116 // RegisterWasmDBFactory registers the wasmdb database factory
117 // This is called from the wasmdb package's init() function
118 func RegisterWasmDBFactory(factory func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)) {
119 newWasmDBDatabase = factory
120 }
121
122 // newGRPCDatabase creates a gRPC client database instance
123 // This is defined here to avoid import cycles
124 var newGRPCDatabase func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)
125
126 // RegisterGRPCFactory registers the gRPC database factory
127 // This is called from the grpc package's init() function
128 func RegisterGRPCFactory(factory func(context.Context, context.CancelFunc, *DatabaseConfig) (Database, error)) {
129 newGRPCDatabase = factory
130 }
131