@plyaz/db 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -0
- package/dist/adapters/drizzle/DrizzleAdapter.d.ts +269 -0
- package/dist/adapters/drizzle/DrizzleAdapter.d.ts.map +1 -0
- package/dist/adapters/index.d.ts +20 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/sql/SQLAdapter.d.ts +282 -0
- package/dist/adapters/sql/SQLAdapter.d.ts.map +1 -0
- package/dist/adapters/supabase/SupabaseAdapter.d.ts +305 -0
- package/dist/adapters/supabase/SupabaseAdapter.d.ts.map +1 -0
- package/dist/advanced/backup/BackupService.d.ts +159 -0
- package/dist/advanced/backup/BackupService.d.ts.map +1 -0
- package/dist/advanced/backup/index.d.ts +2 -0
- package/dist/advanced/backup/index.d.ts.map +1 -0
- package/dist/advanced/caching/CacheEvict.decorator.d.ts +3 -0
- package/dist/advanced/caching/CacheEvict.decorator.d.ts.map +1 -0
- package/dist/advanced/caching/Cacheable.decorator.d.ts +99 -0
- package/dist/advanced/caching/Cacheable.decorator.d.ts.map +1 -0
- package/dist/advanced/caching/RedisCache.d.ts +417 -0
- package/dist/advanced/caching/RedisCache.d.ts.map +1 -0
- package/dist/advanced/caching/index.d.ts +4 -0
- package/dist/advanced/caching/index.d.ts.map +1 -0
- package/dist/advanced/connection-pool/DynamicPool.d.ts +234 -0
- package/dist/advanced/connection-pool/DynamicPool.d.ts.map +1 -0
- package/dist/advanced/connection-pool/index.d.ts +2 -0
- package/dist/advanced/connection-pool/index.d.ts.map +1 -0
- package/dist/advanced/index.d.ts +8 -0
- package/dist/advanced/index.d.ts.map +1 -0
- package/dist/advanced/monitoring/AlertManager.d.ts +72 -0
- package/dist/advanced/monitoring/AlertManager.d.ts.map +1 -0
- package/dist/advanced/monitoring/MetricsCollector.d.ts +81 -0
- package/dist/advanced/monitoring/MetricsCollector.d.ts.map +1 -0
- package/dist/advanced/monitoring/index.d.ts +3 -0
- package/dist/advanced/monitoring/index.d.ts.map +1 -0
- package/dist/advanced/multi-tenancy/TenantContext.d.ts +52 -0
- package/dist/advanced/multi-tenancy/TenantContext.d.ts.map +1 -0
- package/dist/advanced/multi-tenancy/TenantRepository.d.ts +292 -0
- package/dist/advanced/multi-tenancy/TenantRepository.d.ts.map +1 -0
- package/dist/advanced/multi-tenancy/index.d.ts +3 -0
- package/dist/advanced/multi-tenancy/index.d.ts.map +1 -0
- package/dist/advanced/read-replica/ReadReplicaAdapter.d.ts +516 -0
- package/dist/advanced/read-replica/ReadReplicaAdapter.d.ts.map +1 -0
- package/dist/advanced/read-replica/ReadReplicaManager.d.ts +68 -0
- package/dist/advanced/read-replica/ReadReplicaManager.d.ts.map +1 -0
- package/dist/advanced/read-replica/UseReplica.decorator.d.ts +24 -0
- package/dist/advanced/read-replica/UseReplica.decorator.d.ts.map +1 -0
- package/dist/advanced/read-replica/index.d.ts +3 -0
- package/dist/advanced/read-replica/index.d.ts.map +1 -0
- package/dist/advanced/sharding/ShardKey.d.ts +80 -0
- package/dist/advanced/sharding/ShardKey.d.ts.map +1 -0
- package/dist/advanced/sharding/ShardRouter.d.ts +66 -0
- package/dist/advanced/sharding/ShardRouter.d.ts.map +1 -0
- package/dist/advanced/sharding/index.d.ts +3 -0
- package/dist/advanced/sharding/index.d.ts.map +1 -0
- package/dist/builder/query/index.d.ts +7 -0
- package/dist/builder/query/index.d.ts.map +1 -0
- package/dist/builder/query/orm.d.ts +22 -0
- package/dist/builder/query/orm.d.ts.map +1 -0
- package/dist/builder/query/sql.d.ts +29 -0
- package/dist/builder/query/sql.d.ts.map +1 -0
- package/dist/extensions/AuditExtension.d.ts +468 -0
- package/dist/extensions/AuditExtension.d.ts.map +1 -0
- package/dist/extensions/CachingAdapter.d.ts +451 -0
- package/dist/extensions/CachingAdapter.d.ts.map +1 -0
- package/dist/extensions/EncryptionExtension.d.ts +95 -0
- package/dist/extensions/EncryptionExtension.d.ts.map +1 -0
- package/dist/extensions/ReadReplicaAdapter.d.ts +32 -0
- package/dist/extensions/ReadReplicaAdapter.d.ts.map +1 -0
- package/dist/extensions/SoftDeleteExtension.d.ts +430 -0
- package/dist/extensions/SoftDeleteExtension.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +79 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/factory/AdapterFactory.d.ts +111 -0
- package/dist/factory/AdapterFactory.d.ts.map +1 -0
- package/dist/factory/createDatabaseService.d.ts +121 -0
- package/dist/factory/createDatabaseService.d.ts.map +1 -0
- package/dist/index.cjs +8518 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8480 -0
- package/dist/index.mjs.map +1 -0
- package/dist/repository/BaseRepository.d.ts +209 -0
- package/dist/repository/BaseRepository.d.ts.map +1 -0
- package/dist/repository/index.d.ts +80 -0
- package/dist/repository/index.d.ts.map +1 -0
- package/dist/security/index.cjs +118 -0
- package/dist/security/index.cjs.map +1 -0
- package/dist/security/index.d.ts +3 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.mjs +114 -0
- package/dist/security/index.mjs.map +1 -0
- package/dist/security/sanitizers/html.sanitizer.d.ts +31 -0
- package/dist/security/sanitizers/html.sanitizer.d.ts.map +1 -0
- package/dist/security/serializers/DataValidation.d.ts +34 -0
- package/dist/security/serializers/DataValidation.d.ts.map +1 -0
- package/dist/service/DatabaseService.d.ts +136 -0
- package/dist/service/DatabaseService.d.ts.map +1 -0
- package/dist/service/EventEmitter.d.ts +110 -0
- package/dist/service/EventEmitter.d.ts.map +1 -0
- package/dist/service/HealthManager.d.ts +166 -0
- package/dist/service/HealthManager.d.ts.map +1 -0
- package/dist/utils/ConfigMerger.d.ts +227 -0
- package/dist/utils/ConfigMerger.d.ts.map +1 -0
- package/dist/utils/databaseResultHelpers.d.ts +98 -0
- package/dist/utils/databaseResultHelpers.d.ts.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/normalizeDetails.d.ts +111 -0
- package/dist/utils/normalizeDetails.d.ts.map +1 -0
- package/dist/utils/pagination.d.ts +77 -0
- package/dist/utils/pagination.d.ts.map +1 -0
- package/dist/utils/regex.d.ts +199 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/typeGuards.d.ts +57 -0
- package/dist/utils/typeGuards.d.ts.map +1 -0
- package/dist/utils/validation.d.ts +146 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/package.json +156 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import type { DatabaseAdapterType, DatabaseResult, DrizzleAdapterConfig, Filter, DatabaseHealthStatus, PaginatedResult, QueryOptions, Transaction } from "@plyaz/types/db";
|
|
2
|
+
/**
|
|
3
|
+
* Database adapter that provides read replica support with automatic read/write splitting.
|
|
4
|
+
* Routes SELECT queries to read replicas and write operations to the primary database.
|
|
5
|
+
* Implements load balancing across replicas and automatic failover to primary.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const config = {
|
|
10
|
+
* connectionString: 'postgres://primary:5432/db',
|
|
11
|
+
* replicas: [
|
|
12
|
+
* { connectionString: 'postgres://replica1:5432/db' },
|
|
13
|
+
* { connectionString: 'postgres://replica2:5432/db' }
|
|
14
|
+
* ]
|
|
15
|
+
* };
|
|
16
|
+
*
|
|
17
|
+
* const adapter = new ReadReplicaAdapter(config);
|
|
18
|
+
* await adapter.initialize();
|
|
19
|
+
*
|
|
20
|
+
* // This will be routed to a replica
|
|
21
|
+
* const users = await adapter.findMany('users');
|
|
22
|
+
*
|
|
23
|
+
* // This will be routed to primary
|
|
24
|
+
* await adapter.create('users', { name: 'John' });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class ReadReplicaAdapter implements DatabaseAdapterType {
|
|
28
|
+
private primaryAdapter;
|
|
29
|
+
private replicaManager;
|
|
30
|
+
private replicas;
|
|
31
|
+
private currentReplicaIndex;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new ReadReplicaAdapter instance.
|
|
34
|
+
*
|
|
35
|
+
* **RESPONSIBILITY:** Sets up primary adapter and replica manager for read/write splitting
|
|
36
|
+
*
|
|
37
|
+
* @param config Configuration for primary and replica databases
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const adapter = new ReadReplicaAdapter({
|
|
42
|
+
* connectionString: 'postgres://primary:5432/db',
|
|
43
|
+
* replicas: [
|
|
44
|
+
* { connectionString: 'postgres://replica1:5432/db' },
|
|
45
|
+
* { connectionString: 'postgres://replica2:5432/db' }
|
|
46
|
+
* ]
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
constructor(config: DrizzleAdapterConfig & {
|
|
51
|
+
replicas: DrizzleAdapterConfig[];
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Initializes the adapter and all replicas.
|
|
55
|
+
*
|
|
56
|
+
* **RESPONSIBILITY:** Establishes connections to primary and all replica databases
|
|
57
|
+
* **BEHAVIOR:** Fails if primary initialization fails, continues if replicas fail
|
|
58
|
+
*
|
|
59
|
+
* @returns Promise resolving to initialization result
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const adapter = new ReadReplicaAdapter(config);
|
|
64
|
+
* const result = await adapter.initialize();
|
|
65
|
+
*
|
|
66
|
+
* if (result.success) {
|
|
67
|
+
* console.log('All databases initialized successfully');
|
|
68
|
+
* } else {
|
|
69
|
+
* console.error('Initialization failed:', result.error);
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
initialize(): Promise<DatabaseResult<void>>;
|
|
74
|
+
/**
|
|
75
|
+
* Establishes connections to primary and all replicas.
|
|
76
|
+
*
|
|
77
|
+
* **RESPONSIBILITY:** Opens database connections for all configured databases
|
|
78
|
+
* **BEHAVIOR:** Throws error if any connection fails
|
|
79
|
+
*
|
|
80
|
+
* @throws {BaseError} When connection to any database fails
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* try {
|
|
85
|
+
* await adapter.connect();
|
|
86
|
+
* console.log('All connections established');
|
|
87
|
+
* } catch (error) {
|
|
88
|
+
* console.error('Connection failed:', error.message);
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
connect(): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Closes connections to primary and all replicas.
|
|
95
|
+
*
|
|
96
|
+
* **RESPONSIBILITY:** Gracefully closes all database connections
|
|
97
|
+
* **BEHAVIOR:** Attempts to close all connections even if some fail
|
|
98
|
+
*
|
|
99
|
+
* @throws {BaseError} When disconnection from any database fails
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* try {
|
|
104
|
+
* await adapter.disconnect();
|
|
105
|
+
* console.log('All connections closed');
|
|
106
|
+
* } catch (error) {
|
|
107
|
+
* console.error('Disconnection failed:', error.message);
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
disconnect(): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Gets the underlying primary database client.
|
|
114
|
+
*
|
|
115
|
+
* **RESPONSIBILITY:** Provides access to the primary database client for advanced operations
|
|
116
|
+
* **USE CASE:** When you need direct access to database-specific features
|
|
117
|
+
*
|
|
118
|
+
* @returns Primary database client
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* // Get PostgreSQL client for advanced operations
|
|
123
|
+
* const pgClient = adapter.getClient<pg.Client>();
|
|
124
|
+
*
|
|
125
|
+
* // Use client for database-specific operations
|
|
126
|
+
* const result = await pgClient.query('SELECT version()');
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
getClient<TClient extends object>(): TClient;
|
|
130
|
+
/**
|
|
131
|
+
* Executes a raw SQL query with automatic read/write routing.
|
|
132
|
+
*
|
|
133
|
+
* **ROUTING LOGIC:**
|
|
134
|
+
* - SELECT queries → Read replicas (with failover to primary)
|
|
135
|
+
* - INSERT/UPDATE/DELETE → Primary database only
|
|
136
|
+
*
|
|
137
|
+
* **LOAD BALANCING:** Round-robin across healthy replicas
|
|
138
|
+
* **FAILOVER:** Automatic fallback to primary if all replicas fail
|
|
139
|
+
*
|
|
140
|
+
* @param sql SQL query string
|
|
141
|
+
* @param params Query parameters
|
|
142
|
+
* @returns Query result rows
|
|
143
|
+
*
|
|
144
|
+
* @throws {BaseError} When SQL is invalid or execution fails
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* // Read query - routed to replica
|
|
149
|
+
* const users = await adapter.query(
|
|
150
|
+
* 'SELECT * FROM users WHERE status = $1',
|
|
151
|
+
* ['active']
|
|
152
|
+
* );
|
|
153
|
+
*
|
|
154
|
+
* // Write query - routed to primary
|
|
155
|
+
* await adapter.query(
|
|
156
|
+
* 'INSERT INTO users (name, email) VALUES ($1, $2)',
|
|
157
|
+
* ['John Doe', 'john@example.com']
|
|
158
|
+
* );
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
/**
|
|
162
|
+
* Validates SQL query string.
|
|
163
|
+
* @private
|
|
164
|
+
* @param sql SQL query to validate
|
|
165
|
+
* @throws {BaseError} When SQL is not a valid string
|
|
166
|
+
*/
|
|
167
|
+
private validateSql;
|
|
168
|
+
/**
|
|
169
|
+
* Executes query on a read replica with load balancing.
|
|
170
|
+
* @private
|
|
171
|
+
* @param sql SQL query string
|
|
172
|
+
* @param params Query parameters
|
|
173
|
+
* @returns Query result
|
|
174
|
+
*/
|
|
175
|
+
private executeOnReplica;
|
|
176
|
+
/**
|
|
177
|
+
* Tries other replicas or falls back to primary on failure.
|
|
178
|
+
* @private
|
|
179
|
+
* @param sql SQL query string
|
|
180
|
+
* @param params Query parameters
|
|
181
|
+
* @returns Query result
|
|
182
|
+
*/
|
|
183
|
+
private tryOtherReplicasOrPrimary;
|
|
184
|
+
query<T>(sql: string, params?: T[]): Promise<T[]>;
|
|
185
|
+
/**
|
|
186
|
+
* Registers a table with primary and all replicas.
|
|
187
|
+
*
|
|
188
|
+
* **RESPONSIBILITY:** Ensures table schema is available on all database connections
|
|
189
|
+
* **BEHAVIOR:** Registers with primary and all replica adapters
|
|
190
|
+
*
|
|
191
|
+
* @param name Logical table name
|
|
192
|
+
* @param table Table schema
|
|
193
|
+
* @param idColumn Optional ID column name
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* // Register user table with all adapters
|
|
198
|
+
* adapter.registerTable('users', userTableSchema, 'id');
|
|
199
|
+
*
|
|
200
|
+
* // Now all operations on 'users' table will work
|
|
201
|
+
* const user = await adapter.findById('users', '123');
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
registerTable<TTable, TIdColumn>(name: string, table: TTable, idColumn?: TIdColumn): void;
|
|
205
|
+
/**
|
|
206
|
+
* Finds a record by ID, routing to a replica.
|
|
207
|
+
*
|
|
208
|
+
* **ROUTING:** Always uses read replica (with failover to primary)
|
|
209
|
+
* **PERFORMANCE:** Reduces load on primary database
|
|
210
|
+
*
|
|
211
|
+
* @param table Table name
|
|
212
|
+
* @param id Record ID
|
|
213
|
+
* @returns Found record or null
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* // This query goes to a replica
|
|
218
|
+
* const result = await adapter.findById('users', 'user-123');
|
|
219
|
+
*
|
|
220
|
+
* if (result.success && result.value) {
|
|
221
|
+
* console.log('User found:', result.value.name);
|
|
222
|
+
* } else {
|
|
223
|
+
* console.log('User not found');
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
findById<T>(table: string, id: string): Promise<DatabaseResult<T | null>>;
|
|
228
|
+
/**
|
|
229
|
+
* Finds multiple records with filtering and pagination, routing to a replica.
|
|
230
|
+
*
|
|
231
|
+
* **ROUTING:** Always uses read replica (with failover to primary)
|
|
232
|
+
* **PERFORMANCE:** Ideal for heavy read workloads
|
|
233
|
+
*
|
|
234
|
+
* @param table Table name
|
|
235
|
+
* @param options Query options
|
|
236
|
+
* @returns Paginated result
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```typescript
|
|
240
|
+
* // This query goes to a replica
|
|
241
|
+
* const result = await adapter.findMany('users', {
|
|
242
|
+
* filter: { field: 'status', operator: 'eq', value: 'active' },
|
|
243
|
+
* pagination: { page: 1, limit: 10 },
|
|
244
|
+
* sort: { field: 'name', direction: 'asc' }
|
|
245
|
+
* });
|
|
246
|
+
*
|
|
247
|
+
* console.log('Found users:', result.value?.data.length);
|
|
248
|
+
* ```
|
|
249
|
+
*/
|
|
250
|
+
findMany<T>(table: string, options?: QueryOptions): Promise<DatabaseResult<PaginatedResult<T>>>;
|
|
251
|
+
/**
|
|
252
|
+
* Creates a new record, routing to primary.
|
|
253
|
+
*
|
|
254
|
+
* **ROUTING:** Always uses primary database
|
|
255
|
+
* **CONSISTENCY:** Ensures immediate consistency for writes
|
|
256
|
+
*
|
|
257
|
+
* @param table Table name
|
|
258
|
+
* @param data Record data
|
|
259
|
+
* @returns Created record
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```typescript
|
|
263
|
+
* // This operation goes to primary
|
|
264
|
+
* const result = await adapter.create('users', {
|
|
265
|
+
* name: 'John Doe',
|
|
266
|
+
* email: 'john@example.com',
|
|
267
|
+
* status: 'active'
|
|
268
|
+
* });
|
|
269
|
+
*
|
|
270
|
+
* if (result.success) {
|
|
271
|
+
* console.log('User created:', result.value.id);
|
|
272
|
+
* }
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
create<T>(table: string, data: T): Promise<DatabaseResult<T>>;
|
|
276
|
+
/**
|
|
277
|
+
* Updates an existing record, routing to primary.
|
|
278
|
+
*
|
|
279
|
+
* **ROUTING:** Always uses primary database
|
|
280
|
+
* **CONSISTENCY:** Ensures immediate consistency for updates
|
|
281
|
+
*
|
|
282
|
+
* @param table Table name
|
|
283
|
+
* @param id Record ID
|
|
284
|
+
* @param data Partial record data
|
|
285
|
+
* @returns Updated record
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* ```typescript
|
|
289
|
+
* // This operation goes to primary
|
|
290
|
+
* const result = await adapter.update('users', 'user-123', {
|
|
291
|
+
* name: 'Jane Doe',
|
|
292
|
+
* status: 'inactive'
|
|
293
|
+
* });
|
|
294
|
+
*
|
|
295
|
+
* if (result.success) {
|
|
296
|
+
* console.log('User updated:', result.value.name);
|
|
297
|
+
* }
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
update<T>(table: string, id: string, data: Partial<T>): Promise<DatabaseResult<T>>;
|
|
301
|
+
/**
|
|
302
|
+
* Deletes a record, routing to primary.
|
|
303
|
+
*
|
|
304
|
+
* **ROUTING:** Always uses primary database
|
|
305
|
+
* **CONSISTENCY:** Ensures immediate consistency for deletions
|
|
306
|
+
*
|
|
307
|
+
* @param table Table name
|
|
308
|
+
* @param id Record ID
|
|
309
|
+
* @returns Deletion result
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* // This operation goes to primary
|
|
314
|
+
* const result = await adapter.delete('users', 'user-123');
|
|
315
|
+
*
|
|
316
|
+
* if (result.success) {
|
|
317
|
+
* console.log('User deleted successfully');
|
|
318
|
+
* } else {
|
|
319
|
+
* console.error('Delete failed:', result.error?.message);
|
|
320
|
+
* }
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
delete(table: string, id: string): Promise<DatabaseResult<void>>;
|
|
324
|
+
/**
|
|
325
|
+
* Executes a transaction on the primary database.
|
|
326
|
+
*
|
|
327
|
+
* **ROUTING:** Always uses primary database (transactions require consistency)
|
|
328
|
+
* **ACID:** Provides full ACID transaction guarantees
|
|
329
|
+
*
|
|
330
|
+
* @param callback Transaction callback
|
|
331
|
+
* @returns Transaction result
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```typescript
|
|
335
|
+
* // Transaction always goes to primary
|
|
336
|
+
* const result = await adapter.transaction(async (trx) => {
|
|
337
|
+
* const user = await trx.create('users', { name: 'John' });
|
|
338
|
+
* await trx.create('profiles', { userId: user.id, bio: 'Hello' });
|
|
339
|
+
* return user;
|
|
340
|
+
* });
|
|
341
|
+
*
|
|
342
|
+
* if (result.success) {
|
|
343
|
+
* console.log('Transaction completed:', result.value.id);
|
|
344
|
+
* }
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
347
|
+
transaction<T>(callback: (trx: Transaction) => Promise<T>): Promise<DatabaseResult<T>>;
|
|
348
|
+
/**
|
|
349
|
+
* Checks if a record exists, routing to a replica.
|
|
350
|
+
*
|
|
351
|
+
* **ROUTING:** Uses read replica (with failover to primary)
|
|
352
|
+
* **PERFORMANCE:** Efficient existence check without full record retrieval
|
|
353
|
+
*
|
|
354
|
+
* @param table Table name
|
|
355
|
+
* @param id Record ID
|
|
356
|
+
* @returns Existence result
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```typescript
|
|
360
|
+
* // This check goes to a replica
|
|
361
|
+
* const result = await adapter.exists('users', 'user-123');
|
|
362
|
+
*
|
|
363
|
+
* if (result.success && result.value) {
|
|
364
|
+
* console.log('User exists');
|
|
365
|
+
* } else {
|
|
366
|
+
* console.log('User does not exist');
|
|
367
|
+
* }
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
exists(table: string, id: string): Promise<DatabaseResult<boolean>>;
|
|
371
|
+
/**
|
|
372
|
+
* Counts records matching a filter, routing to a replica.
|
|
373
|
+
*
|
|
374
|
+
* **ROUTING:** Uses read replica (with failover to primary)
|
|
375
|
+
* **PERFORMANCE:** Efficient counting without data transfer
|
|
376
|
+
*
|
|
377
|
+
* @param table Table name
|
|
378
|
+
* @param filter Filter conditions
|
|
379
|
+
* @returns Count result
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```typescript
|
|
383
|
+
* // This count goes to a replica
|
|
384
|
+
* const result = await adapter.count('users', {
|
|
385
|
+
* field: 'status',
|
|
386
|
+
* operator: 'eq',
|
|
387
|
+
* value: 'active'
|
|
388
|
+
* });
|
|
389
|
+
*
|
|
390
|
+
* if (result.success) {
|
|
391
|
+
* console.log('Active users:', result.value);
|
|
392
|
+
* }
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
count(table: string, filter?: Filter): Promise<DatabaseResult<number>>;
|
|
396
|
+
/**
|
|
397
|
+
* Performs health checks on primary and all replicas.
|
|
398
|
+
*
|
|
399
|
+
* **RESPONSIBILITY:** Monitors health of entire database cluster
|
|
400
|
+
* **BEHAVIOR:** Returns healthy only if primary and at least one replica are healthy
|
|
401
|
+
*
|
|
402
|
+
* @returns Combined health status
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```typescript
|
|
406
|
+
* const result = await adapter.healthCheck();
|
|
407
|
+
*
|
|
408
|
+
* if (result.success && result.value?.isHealthy) {
|
|
409
|
+
* console.log('Cluster is healthy');
|
|
410
|
+
* console.log('Response time:', result.value.responseTime, 'ms');
|
|
411
|
+
* } else {
|
|
412
|
+
* console.error('Cluster health issues detected');
|
|
413
|
+
* }
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
healthCheck(): Promise<DatabaseResult<DatabaseHealthStatus>>;
|
|
417
|
+
/**
|
|
418
|
+
* Determines if a SQL operation is a write operation.
|
|
419
|
+
*
|
|
420
|
+
* **LOGIC:** Checks for INSERT, UPDATE, DELETE, CREATE, DROP, ALTER keywords
|
|
421
|
+
* **ROUTING:** Write operations go to primary, reads go to replicas
|
|
422
|
+
*
|
|
423
|
+
* @private
|
|
424
|
+
* @param sql SQL query string
|
|
425
|
+
* @returns True if operation is a write operation
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```typescript
|
|
429
|
+
* // These return true (write operations)
|
|
430
|
+
* this.isWriteOperation('INSERT INTO users...') // true
|
|
431
|
+
* this.isWriteOperation('UPDATE users SET...') // true
|
|
432
|
+
* this.isWriteOperation('DELETE FROM users...') // true
|
|
433
|
+
*
|
|
434
|
+
* // These return false (read operations)
|
|
435
|
+
* this.isWriteOperation('SELECT * FROM users') // false
|
|
436
|
+
* this.isWriteOperation('SELECT COUNT(*) FROM users') // false
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
private isWriteOperation;
|
|
440
|
+
/**
|
|
441
|
+
* Routes a read operation to a healthy replica with load balancing.
|
|
442
|
+
*
|
|
443
|
+
* **RESPONSIBILITY:** Executes operation on replica with automatic failover
|
|
444
|
+
* **LOAD BALANCING:** Uses round-robin selection across healthy replicas
|
|
445
|
+
* **FAILOVER:** Automatically tries handleReplicaError on failure
|
|
446
|
+
*
|
|
447
|
+
* @private
|
|
448
|
+
* @param operation Function to execute on replica adapter
|
|
449
|
+
* @returns Operation result
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* // Internal usage for read operations
|
|
454
|
+
* const result = await this.routeToReplica(adapter =>
|
|
455
|
+
* adapter.findById('users', 'user-123')
|
|
456
|
+
* );
|
|
457
|
+
*
|
|
458
|
+
* // Automatically handles:
|
|
459
|
+
* // 1. Selects healthy replica using round-robin
|
|
460
|
+
* // 2. Executes operation on selected replica
|
|
461
|
+
* // 3. Falls back to handleReplicaError on failure
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
private routeToReplica;
|
|
465
|
+
/**
|
|
466
|
+
* Handles replica errors by trying other replicas or falling back to primary.
|
|
467
|
+
*
|
|
468
|
+
* **RESPONSIBILITY:** Implements failover strategy when replica operations fail
|
|
469
|
+
* **STRATEGY:** Try all remaining replicas, then fallback to primary
|
|
470
|
+
* **RESILIENCE:** Ensures operation completes even if all replicas fail
|
|
471
|
+
*
|
|
472
|
+
* @private
|
|
473
|
+
* @param operation Function to execute on adapter
|
|
474
|
+
* @returns Operation result
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```typescript
|
|
478
|
+
* // Internal usage when replica fails
|
|
479
|
+
* const result = await this.handleReplicaError(adapter =>
|
|
480
|
+
* adapter.findMany('users', options)
|
|
481
|
+
* );
|
|
482
|
+
*
|
|
483
|
+
* // Failover sequence:
|
|
484
|
+
* // 1. Try each remaining healthy replica
|
|
485
|
+
* // 2. If all replicas fail, use primary adapter
|
|
486
|
+
* // 3. Return result from successful operation
|
|
487
|
+
* ```
|
|
488
|
+
*/
|
|
489
|
+
private handleReplicaError;
|
|
490
|
+
/**
|
|
491
|
+
* Gets healthy replicas by checking their status.
|
|
492
|
+
*
|
|
493
|
+
* **RESPONSIBILITY:** Filters replicas based on health status
|
|
494
|
+
* **CURRENT:** Returns all replicas (health checking not implemented)
|
|
495
|
+
* **FUTURE:** Should implement actual health status checking
|
|
496
|
+
*
|
|
497
|
+
* @private
|
|
498
|
+
* @returns Array of healthy replica adapters
|
|
499
|
+
*
|
|
500
|
+
* @example
|
|
501
|
+
* ```typescript
|
|
502
|
+
* // Internal usage
|
|
503
|
+
* const healthyReplicas = this.getHealthyReplicas();
|
|
504
|
+
*
|
|
505
|
+
* if (healthyReplicas.length > 0) {
|
|
506
|
+
* // Use replica for read operation
|
|
507
|
+
* const replica = healthyReplicas[index % healthyReplicas.length];
|
|
508
|
+
* } else {
|
|
509
|
+
* // No healthy replicas, fallback to primary
|
|
510
|
+
* return this.primaryAdapter.operation();
|
|
511
|
+
* }
|
|
512
|
+
* ```
|
|
513
|
+
*/
|
|
514
|
+
private getHealthyReplicas;
|
|
515
|
+
}
|
|
516
|
+
//# sourceMappingURL=ReadReplicaAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadReplicaAdapter.d.ts","sourceRoot":"","sources":["../../../src/advanced/read-replica/ReadReplicaAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { DrizzleAdapter } from "@adapters/drizzle/DrizzleAdapter";
|
|
2
|
+
import type { DatabaseResult, DrizzleAdapterConfig, DatabaseHealthStatus } from "@plyaz/types/db";
|
|
3
|
+
/**
|
|
4
|
+
* Manages read replica connections and health monitoring.
|
|
5
|
+
* Provides load balancing and failover capabilities across multiple replicas.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const configs = [
|
|
10
|
+
* { connectionString: 'postgres://replica1:5432/db' },
|
|
11
|
+
* { connectionString: 'postgres://replica2:5432/db' }
|
|
12
|
+
* ];
|
|
13
|
+
*
|
|
14
|
+
* const manager = new ReadReplicaManager(configs);
|
|
15
|
+
* await manager.initialize();
|
|
16
|
+
*
|
|
17
|
+
* const healthyReplicas = manager.getHealthyReplicas();
|
|
18
|
+
* const health = await manager.healthCheck();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare class ReadReplicaManager {
|
|
22
|
+
private replicaConfigs;
|
|
23
|
+
private replicas;
|
|
24
|
+
private replicaHealth;
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new ReadReplicaManager instance.
|
|
27
|
+
* @param replicaConfigs Array of replica configurations
|
|
28
|
+
*/
|
|
29
|
+
constructor(replicaConfigs: DrizzleAdapterConfig[]);
|
|
30
|
+
/**
|
|
31
|
+
* Initializes all replicas and checks their health.
|
|
32
|
+
* @returns Initialization result
|
|
33
|
+
*/
|
|
34
|
+
initialize(): Promise<DatabaseResult<void>>;
|
|
35
|
+
/**
|
|
36
|
+
* Establishes connections to all replicas.
|
|
37
|
+
*/
|
|
38
|
+
connect(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Closes connections to all replicas.
|
|
41
|
+
*/
|
|
42
|
+
disconnect(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Registers a table with all replicas.
|
|
45
|
+
* @param name Logical table name
|
|
46
|
+
* @param table Table schema
|
|
47
|
+
* @param idColumn Optional ID column name
|
|
48
|
+
*/
|
|
49
|
+
registerTable<TTable, TIdColumn>(name: string, table: TTable, idColumn?: TIdColumn): void;
|
|
50
|
+
/**
|
|
51
|
+
* Performs health checks on all replicas.
|
|
52
|
+
* @returns Combined health status
|
|
53
|
+
*/
|
|
54
|
+
healthCheck(): Promise<DatabaseResult<DatabaseHealthStatus>>;
|
|
55
|
+
/**
|
|
56
|
+
* Gets all currently healthy replicas.
|
|
57
|
+
* @returns Array of healthy replica adapters
|
|
58
|
+
*/
|
|
59
|
+
getHealthyReplicas(): DrizzleAdapter[];
|
|
60
|
+
/**
|
|
61
|
+
* Checks the health of a specific replica.
|
|
62
|
+
* @param replica Replica adapter to check
|
|
63
|
+
* @param index Replica index
|
|
64
|
+
* @returns Health check result
|
|
65
|
+
*/
|
|
66
|
+
private checkReplicaHealth;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=ReadReplicaManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadReplicaManager.d.ts","sourceRoot":"","sources":["../../../src/advanced/read-replica/ReadReplicaManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAGlE,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AACzB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,kBAAkB;IAQjB,OAAO,CAAC,cAAc;IAPlC,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,aAAa,CAAmC;IAExD;;;OAGG;gBACiB,cAAc,EAAE,oBAAoB,EAAE;IAE1D;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;IA0CjD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,SAAS,EAC7B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,SAAS,GACnB,IAAI;IAMP;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;IAuClE;;;OAGG;IACH,kBAAkB,IAAI,cAAc,EAAE;IAItC;;;;;OAKG;YACW,kBAAkB;CAwBjC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { UseReplicaOptions } from "@plyaz/types";
|
|
2
|
+
export declare const USE_REPLICA_KEY = "use_replica";
|
|
3
|
+
/**
|
|
4
|
+
* Decorator to specify replica usage for database operations.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* class UserService {
|
|
9
|
+
* @UseReplica({ strategy: ReplicaStrategy.REPLICA })
|
|
10
|
+
* async getUsers() {
|
|
11
|
+
* // This will use a read replica
|
|
12
|
+
* return this.db.findMany('users');
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* @UseReplica({ strategy: ReplicaStrategy.PRIMARY })
|
|
16
|
+
* async createUser(user: User) {
|
|
17
|
+
* // This will use primary
|
|
18
|
+
* return this.db.create('users', user);
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function UseReplica(options?: UseReplicaOptions): MethodDecorator;
|
|
24
|
+
//# sourceMappingURL=UseReplica.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UseReplica.decorator.d.ts","sourceRoot":"","sources":["../../../src/advanced/read-replica/UseReplica.decorator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtD,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,eAAe,CAI3E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/advanced/read-replica/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC"}
|