@bhushanpawar/sqldb 1.0.0 → 1.0.1
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 +695 -4
- package/dist/cache/cache-manager.d.ts.map +1 -1
- package/dist/cache/cache-manager.js +2 -1
- package/dist/cache/cache-manager.js.map +1 -1
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +225 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/schema-generator.d.ts +28 -0
- package/dist/cli/schema-generator.d.ts.map +1 -0
- package/dist/cli/schema-generator.js +159 -0
- package/dist/cli/schema-generator.js.map +1 -0
- package/dist/client.d.ts +32 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +56 -14
- package/dist/client.js.map +1 -1
- package/dist/connection/mariadb.d.ts +21 -0
- package/dist/connection/mariadb.d.ts.map +1 -1
- package/dist/connection/mariadb.js +121 -3
- package/dist/connection/mariadb.js.map +1 -1
- package/dist/db-schema.d.ts +413 -0
- package/dist/db-schema.d.ts.map +1 -0
- package/dist/db-schema.js +1149 -0
- package/dist/db-schema.js.map +1 -0
- package/dist/discovery/dependency-graph.d.ts +3 -0
- package/dist/discovery/dependency-graph.d.ts.map +1 -1
- package/dist/discovery/dependency-graph.js +11 -0
- package/dist/discovery/dependency-graph.js.map +1 -1
- package/dist/discovery/schema-reader.d.ts +1 -1
- package/dist/discovery/schema-reader.d.ts.map +1 -1
- package/dist/discovery/schema-reader.js +48 -19
- package/dist/discovery/schema-reader.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -3
- package/dist/index.js.map +1 -1
- package/dist/query/operations.d.ts +7 -0
- package/dist/query/operations.d.ts.map +1 -1
- package/dist/query/operations.js +204 -22
- package/dist/query/operations.js.map +1 -1
- package/dist/types/client.d.ts +32 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/client.js +3 -0
- package/dist/types/client.js.map +1 -0
- package/dist/types/config.d.ts +3 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +14 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/query.d.ts +12 -0
- package/dist/types/query.d.ts.map +1 -1
- package/dist/types/schema.d.ts +6 -0
- package/dist/types/schema.d.ts.map +1 -1
- package/dist/types/warming.d.ts +47 -0
- package/dist/types/warming.d.ts.map +1 -0
- package/dist/types/warming.js +3 -0
- package/dist/types/warming.js.map +1 -0
- package/dist/warming/auto-warming-manager.d.ts +65 -0
- package/dist/warming/auto-warming-manager.d.ts.map +1 -0
- package/dist/warming/auto-warming-manager.js +256 -0
- package/dist/warming/auto-warming-manager.js.map +1 -0
- package/dist/warming/query-stats-tracker.d.ts +53 -0
- package/dist/warming/query-stats-tracker.d.ts.map +1 -0
- package/dist/warming/query-stats-tracker.js +273 -0
- package/dist/warming/query-stats-tracker.js.map +1 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -80,6 +80,57 @@ await users.deleteById(1);
|
|
|
80
80
|
await db.close();
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Singleton Pattern (Recommended)
|
|
84
|
+
|
|
85
|
+
For production applications, use singleton mode to share a single connection pool:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { createSmartDB, getSmartDB } from '@bhushanpawar/sqldb';
|
|
89
|
+
|
|
90
|
+
// Initialize once at app startup
|
|
91
|
+
const db = await createSmartDB({
|
|
92
|
+
mariadb: { /* config */ },
|
|
93
|
+
redis: { /* config */ },
|
|
94
|
+
cache: { enabled: true },
|
|
95
|
+
}, { singleton: true }); // Enable singleton mode
|
|
96
|
+
|
|
97
|
+
// Access anywhere in your app
|
|
98
|
+
import { getSmartDB } from '@bhushanpawar/sqldb';
|
|
99
|
+
|
|
100
|
+
const db = getSmartDB(); // Returns the same instance
|
|
101
|
+
const users = db.getTableOperations('users');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
See [SINGLETON_PATTERN.md](./docs/SINGLETON_PATTERN.md) for detailed usage.
|
|
105
|
+
|
|
106
|
+
### Dynamic Table Access (TypeScript-Friendly)
|
|
107
|
+
|
|
108
|
+
Access tables directly as properties with full type safety:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { createSmartDB, SmartDBWithTables } from '@bhushanpawar/sqldb';
|
|
112
|
+
|
|
113
|
+
// Define your schema
|
|
114
|
+
interface MySchema {
|
|
115
|
+
users: { id: number; name: string; email: string };
|
|
116
|
+
orders: { id: number; user_id: number; total: number };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type MyDB = SmartDBWithTables<MySchema>;
|
|
120
|
+
|
|
121
|
+
const db = await createSmartDB(config) as MyDB;
|
|
122
|
+
|
|
123
|
+
// Clean, typed access
|
|
124
|
+
const users = await db.users.findMany(); // Type: MySchema['users'][]
|
|
125
|
+
const order = await db.orders.findById(123); // Type: MySchema['orders'] | null
|
|
126
|
+
await db.users.updateById(1, { name: 'Jane' }); // Fully type-checked
|
|
127
|
+
|
|
128
|
+
// Still works the old way too
|
|
129
|
+
const usersTable = db.getTableOperations('users');
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
See [DYNAMIC_TABLE_ACCESS.md](./docs/DYNAMIC_TABLE_ACCESS.md) for detailed usage.
|
|
133
|
+
|
|
83
134
|
### Raw Query Caching
|
|
84
135
|
|
|
85
136
|
The `raw` method supports caching custom SQL queries with a configurable TTL (default: 1 minute):
|
|
@@ -432,6 +483,48 @@ await users.warmCache({ status: 'active' });
|
|
|
432
483
|
const active = await users.findMany({ status: 'active' });
|
|
433
484
|
```
|
|
434
485
|
|
|
486
|
+
### Cache Warming with Relations
|
|
487
|
+
|
|
488
|
+
Pre-warm cache for a table and all its related tables based on the dependency graph:
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
const provider = db.getTableOperations('provider');
|
|
492
|
+
|
|
493
|
+
// Warm cache for provider and all related tables
|
|
494
|
+
await provider.warmCacheWithRelations({}, {
|
|
495
|
+
correlationId: 'startup-warming',
|
|
496
|
+
depth: 1, // How deep to traverse relationships
|
|
497
|
+
warmDependents: true, // Warm tables that reference this table
|
|
498
|
+
warmDependencies: true, // Warm tables this table references
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Now provider and all related tables are cached:
|
|
502
|
+
// - provider (main table)
|
|
503
|
+
// - user (table that provider depends on)
|
|
504
|
+
// - orders, services, bank_details, etc. (tables that depend on provider)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**Use Cases:**
|
|
508
|
+
- **Application startup**: Pre-warm frequently accessed tables and their relations
|
|
509
|
+
- **API endpoints**: Warm cache before handling requests for better response times
|
|
510
|
+
- **Batch operations**: Pre-load related data before processing
|
|
511
|
+
|
|
512
|
+
**Example - Warm on Startup:**
|
|
513
|
+
```typescript
|
|
514
|
+
async function warmCacheOnStartup(db: SmartDBClient) {
|
|
515
|
+
// Warm most frequently accessed tables with their relations
|
|
516
|
+
const provider = db.getTableOperations('provider');
|
|
517
|
+
const orders = db.getTableOperations('orders');
|
|
518
|
+
|
|
519
|
+
await Promise.all([
|
|
520
|
+
provider.warmCacheWithRelations({}, { depth: 1, warmDependents: true }),
|
|
521
|
+
orders.warmCacheWithRelations({}, { depth: 1, warmDependencies: true }),
|
|
522
|
+
]);
|
|
523
|
+
|
|
524
|
+
console.log('Cache warmed successfully!');
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
435
528
|
## Query Tracking
|
|
436
529
|
|
|
437
530
|
Track queries with correlation IDs for debugging and performance monitoring:
|
|
@@ -769,15 +862,613 @@ Based on real-world testing:
|
|
|
769
862
|
- **Throughput**: 10,000+ queries/second with Redis cache
|
|
770
863
|
- **Memory**: ~1KB per cached query
|
|
771
864
|
|
|
772
|
-
See [PERFORMANCE_RESULTS.md](./PERFORMANCE_RESULTS.md) for detailed benchmarks.
|
|
865
|
+
See [PERFORMANCE_RESULTS.md](./docs/PERFORMANCE_RESULTS.md) for detailed benchmarks.
|
|
773
866
|
|
|
774
867
|
## Examples
|
|
775
868
|
|
|
776
|
-
|
|
869
|
+
This section provides examples from simple to complex, helping you get started quickly and gradually explore advanced features.
|
|
870
|
+
|
|
871
|
+
### 1. Hello World - Minimal Setup
|
|
872
|
+
|
|
873
|
+
The simplest way to get started with SmartDB:
|
|
874
|
+
|
|
875
|
+
```typescript
|
|
876
|
+
import { createSmartDB } from '@bhushanpawar/sqldb';
|
|
877
|
+
|
|
878
|
+
// Initialize with minimal config
|
|
879
|
+
const db = await createSmartDB({
|
|
880
|
+
mariadb: {
|
|
881
|
+
host: 'localhost',
|
|
882
|
+
user: 'root',
|
|
883
|
+
password: 'password',
|
|
884
|
+
database: 'mydb',
|
|
885
|
+
},
|
|
886
|
+
redis: {
|
|
887
|
+
host: 'localhost',
|
|
888
|
+
},
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
// Query users - automatically cached!
|
|
892
|
+
const users = await (db as any).users.findMany();
|
|
893
|
+
console.log('Found users:', users.length);
|
|
894
|
+
|
|
895
|
+
await db.close();
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
**What this does:**
|
|
899
|
+
- Connects to MariaDB and Redis
|
|
900
|
+
- Auto-discovers all tables in your database
|
|
901
|
+
- Enables caching with smart defaults (60s TTL)
|
|
902
|
+
- Provides simple CRUD operations
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
### 2. Basic CRUD Operations
|
|
907
|
+
|
|
908
|
+
Learn all the basic operations with caching:
|
|
909
|
+
|
|
910
|
+
```typescript
|
|
911
|
+
import { createSmartDB } from '@bhushanpawar/sqldb';
|
|
912
|
+
|
|
913
|
+
const db = await createSmartDB({
|
|
914
|
+
mariadb: { host: 'localhost', user: 'root', password: 'password', database: 'mydb' },
|
|
915
|
+
redis: { host: 'localhost' },
|
|
916
|
+
cache: {
|
|
917
|
+
enabled: true,
|
|
918
|
+
defaultTTL: 60,
|
|
919
|
+
invalidateOnWrite: true, // Auto-clear cache on INSERT/UPDATE/DELETE
|
|
920
|
+
},
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
// READ operations (cached automatically)
|
|
924
|
+
const allUsers = await (db as any).users.findMany();
|
|
925
|
+
const activeUsers = await (db as any).users.findMany({ status: 'active' });
|
|
926
|
+
const user = await (db as any).users.findById(1);
|
|
927
|
+
const count = await (db as any).users.count({ status: 'active' });
|
|
928
|
+
|
|
929
|
+
// CREATE operations (invalidates cache)
|
|
930
|
+
const newUser = await (db as any).users.insertOne({
|
|
931
|
+
name: 'John Doe',
|
|
932
|
+
email: 'john@example.com',
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
// UPDATE operations (invalidates cache)
|
|
936
|
+
await (db as any).users.updateById(1, { status: 'verified' });
|
|
937
|
+
await (db as any).users.updateMany({ status: 'pending' }, { status: 'active' });
|
|
938
|
+
|
|
939
|
+
// DELETE operations (invalidates cache)
|
|
940
|
+
await (db as any).users.deleteById(1);
|
|
941
|
+
await (db as any).users.deleteMany({ status: 'inactive' });
|
|
942
|
+
|
|
943
|
+
// Check cache performance
|
|
944
|
+
const stats = db.getCacheManager().getStats();
|
|
945
|
+
console.log('Cache hit rate:', stats.hitRate);
|
|
946
|
+
|
|
947
|
+
await db.close();
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
**New concepts:**
|
|
951
|
+
- Automatic cache invalidation on writes
|
|
952
|
+
- Multiple find/update/delete methods
|
|
953
|
+
- Cache statistics monitoring
|
|
954
|
+
|
|
955
|
+
**See:** [basic-usage.ts](./examples/basic-usage.ts)
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
### 3. Type-Safe Queries with TypeScript
|
|
960
|
+
|
|
961
|
+
Add full type safety to your queries:
|
|
962
|
+
|
|
963
|
+
```typescript
|
|
964
|
+
import { createSmartDB, SmartDBWithTables } from '@bhushanpawar/sqldb';
|
|
965
|
+
|
|
966
|
+
// Define your schema
|
|
967
|
+
interface User {
|
|
968
|
+
id: number;
|
|
969
|
+
name: string;
|
|
970
|
+
email: string;
|
|
971
|
+
status: 'active' | 'inactive' | 'verified';
|
|
972
|
+
created_at: Date;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
interface Order {
|
|
976
|
+
id: number;
|
|
977
|
+
user_id: number;
|
|
978
|
+
total: number;
|
|
979
|
+
status: string;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
interface MySchema {
|
|
983
|
+
users: User;
|
|
984
|
+
orders: Order;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
// Create typed DB instance
|
|
988
|
+
type MyDB = SmartDBWithTables<MySchema>;
|
|
989
|
+
const db = await createSmartDB(config) as MyDB;
|
|
990
|
+
|
|
991
|
+
// Full type safety!
|
|
992
|
+
const users = await db.users.findMany(); // Type: User[]
|
|
993
|
+
const user = await db.users.findById(1); // Type: User | null
|
|
994
|
+
await db.users.updateById(1, { status: 'verified' }); // Type-checked!
|
|
995
|
+
|
|
996
|
+
// TypeScript will catch errors
|
|
997
|
+
// await db.users.updateById(1, { invalid: 'field' }); // ❌ Error!
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
**New concepts:**
|
|
1001
|
+
- TypeScript interfaces for your schema
|
|
1002
|
+
- Compile-time type checking
|
|
1003
|
+
- Auto-completion in your IDE
|
|
1004
|
+
|
|
1005
|
+
**See:** [typed-tables-example.ts](./examples/typed-tables-example.ts), [DYNAMIC_TABLE_ACCESS.md](./docs/DYNAMIC_TABLE_ACCESS.md)
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
### 4. Query Tracking & Performance Monitoring
|
|
1010
|
+
|
|
1011
|
+
Track query performance with correlation IDs:
|
|
1012
|
+
|
|
1013
|
+
```typescript
|
|
1014
|
+
import { createSmartDB, generateQueryId } from '@bhushanpawar/sqldb';
|
|
1015
|
+
|
|
1016
|
+
const db = await createSmartDB({
|
|
1017
|
+
mariadb: { /* config */ },
|
|
1018
|
+
redis: { /* config */ },
|
|
1019
|
+
logging: { level: 'info' },
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
// Generate a correlation ID (e.g., per HTTP request)
|
|
1023
|
+
const correlationId = generateQueryId();
|
|
1024
|
+
|
|
1025
|
+
// All queries with same correlationId are tracked together
|
|
1026
|
+
const users = await (db as any).users.findMany(
|
|
1027
|
+
{ status: 'active' },
|
|
1028
|
+
{ correlationId }
|
|
1029
|
+
);
|
|
1030
|
+
|
|
1031
|
+
const count = await (db as any).users.count(
|
|
1032
|
+
{ status: 'active' },
|
|
1033
|
+
correlationId
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
// Analyze performance
|
|
1037
|
+
const queries = db.getQueries(correlationId);
|
|
1038
|
+
queries.forEach(q => {
|
|
1039
|
+
console.log({
|
|
1040
|
+
table: q.sql.match(/FROM (\w+)/)?.[1],
|
|
1041
|
+
executionTime: q.executionTimeMs + 'ms',
|
|
1042
|
+
cached: q.resultCount,
|
|
1043
|
+
});
|
|
1044
|
+
});
|
|
1045
|
+
|
|
1046
|
+
// Calculate total time
|
|
1047
|
+
const totalTime = queries.reduce((sum, q) => sum + (q.executionTimeMs || 0), 0);
|
|
1048
|
+
console.log(`Total query time: ${totalTime}ms`);
|
|
1049
|
+
|
|
1050
|
+
// Clean up
|
|
1051
|
+
db.clearQueries(correlationId);
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
**New concepts:**
|
|
1055
|
+
- Correlation IDs for request tracking
|
|
1056
|
+
- Query performance analysis
|
|
1057
|
+
- Debugging slow requests
|
|
1058
|
+
|
|
1059
|
+
**Use cases:**
|
|
1060
|
+
- HTTP request tracking
|
|
1061
|
+
- Performance monitoring
|
|
1062
|
+
- Identifying slow queries
|
|
1063
|
+
|
|
1064
|
+
**See:** [query-tracking.ts](./examples/query-tracking.ts), [QUERY_TRACKING.md](./docs/QUERY_TRACKING.md)
|
|
1065
|
+
|
|
1066
|
+
---
|
|
1067
|
+
|
|
1068
|
+
### 5. Enhanced Query Logging
|
|
1069
|
+
|
|
1070
|
+
Monitor all database queries with detailed logging:
|
|
1071
|
+
|
|
1072
|
+
```typescript
|
|
1073
|
+
import { createSmartDB } from '@bhushanpawar/sqldb';
|
|
1074
|
+
|
|
1075
|
+
const db = await createSmartDB({
|
|
1076
|
+
mariadb: {
|
|
1077
|
+
host: 'localhost',
|
|
1078
|
+
user: 'root',
|
|
1079
|
+
password: 'password',
|
|
1080
|
+
database: 'mydb',
|
|
1081
|
+
logging: true, // Enable query logging
|
|
1082
|
+
},
|
|
1083
|
+
redis: { host: 'localhost' },
|
|
1084
|
+
logging: { level: 'info' },
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
// Run queries - they'll be logged automatically
|
|
1088
|
+
const users = await (db as any).users.findMany({ status: 'active' });
|
|
1089
|
+
const count = await (db as any).users.count({});
|
|
1090
|
+
|
|
1091
|
+
// Console output shows:
|
|
1092
|
+
// ✅ SELECT on users - 45ms - 10 rows
|
|
1093
|
+
// 🚀 SELECT on users - 12ms - 1 rows
|
|
1094
|
+
// ⚠️ SELECT on orders - 250ms - 100 rows (shows SQL for slow queries)
|
|
1095
|
+
```
|
|
1096
|
+
|
|
1097
|
+
**Logging features:**
|
|
1098
|
+
- Query type (SELECT, INSERT, UPDATE, DELETE)
|
|
1099
|
+
- Table name extraction
|
|
1100
|
+
- Execution time with performance emojis
|
|
1101
|
+
- Automatic SQL display for slow queries (>200ms)
|
|
1102
|
+
|
|
1103
|
+
**Performance emojis:**
|
|
1104
|
+
- ⚡ Very fast (<10ms)
|
|
1105
|
+
- 🚀 Fast (<50ms)
|
|
1106
|
+
- ✅ Good (<200ms)
|
|
1107
|
+
- ⚠️ Slow (<500ms)
|
|
1108
|
+
- 🐌 Very slow (≥500ms)
|
|
1109
|
+
|
|
1110
|
+
**See:** [query-logging-example.ts](./examples/query-logging-example.ts), [QUERY_LOGGING.md](./docs/QUERY_LOGGING.md)
|
|
1111
|
+
|
|
1112
|
+
---
|
|
1113
|
+
|
|
1114
|
+
### 6. Smart Cache Invalidation with Relations
|
|
1115
|
+
|
|
1116
|
+
Automatic cascade invalidation based on foreign keys:
|
|
1117
|
+
|
|
1118
|
+
```typescript
|
|
1119
|
+
// Database schema:
|
|
1120
|
+
// users (id, name)
|
|
1121
|
+
// posts (id, user_id, title) ← FK to users
|
|
1122
|
+
// comments (id, post_id, content) ← FK to posts
|
|
1123
|
+
|
|
1124
|
+
const db = await createSmartDB({
|
|
1125
|
+
mariadb: { /* config */ },
|
|
1126
|
+
redis: { /* config */ },
|
|
1127
|
+
cache: {
|
|
1128
|
+
enabled: true,
|
|
1129
|
+
invalidateOnWrite: true,
|
|
1130
|
+
cascadeInvalidation: true, // Enable cascade invalidation
|
|
1131
|
+
},
|
|
1132
|
+
discovery: {
|
|
1133
|
+
autoDiscover: true, // Auto-discover relationships
|
|
1134
|
+
},
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
// When you update a user...
|
|
1138
|
+
await (db as any).users.updateById(1, { name: 'Updated Name' });
|
|
1139
|
+
|
|
1140
|
+
// SmartDB automatically invalidates:
|
|
1141
|
+
// 1. users:* (direct table)
|
|
1142
|
+
// 2. posts:* (depends on users via user_id)
|
|
1143
|
+
// 3. comments:* (depends on posts via post_id)
|
|
1144
|
+
|
|
1145
|
+
// View the dependency graph
|
|
1146
|
+
const graph = db.getDependencyGraph();
|
|
1147
|
+
const dependencies = graph.getDependencies('users');
|
|
1148
|
+
console.log('Tables that depend on users:', dependencies); // ['posts', 'comments']
|
|
1149
|
+
|
|
1150
|
+
// Manual invalidation with cascade
|
|
1151
|
+
const invalidationManager = db.getInvalidationManager();
|
|
1152
|
+
await invalidationManager.invalidateTable('users', { cascade: true });
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
**New concepts:**
|
|
1156
|
+
- Automatic relationship discovery
|
|
1157
|
+
- Cascade cache invalidation
|
|
1158
|
+
- Dependency graph visualization
|
|
1159
|
+
|
|
1160
|
+
**See:** [relationships-example.ts](./examples/relationships-example.ts)
|
|
1161
|
+
|
|
1162
|
+
---
|
|
1163
|
+
|
|
1164
|
+
### 7. Singleton Pattern for Production
|
|
1165
|
+
|
|
1166
|
+
Share a single SmartDB instance across your entire application:
|
|
1167
|
+
|
|
1168
|
+
```typescript
|
|
1169
|
+
// db.ts - Initialize once at app startup
|
|
1170
|
+
import { createSmartDB } from '@bhushanpawar/sqldb';
|
|
1171
|
+
|
|
1172
|
+
export const initializeDB = async () => {
|
|
1173
|
+
const db = await createSmartDB({
|
|
1174
|
+
mariadb: { /* config */ },
|
|
1175
|
+
redis: { /* config */ },
|
|
1176
|
+
cache: { enabled: true },
|
|
1177
|
+
}, { singleton: true }); // Enable singleton mode
|
|
1178
|
+
|
|
1179
|
+
return db;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
// server.ts - Initialize at startup
|
|
1183
|
+
import { initializeDB } from './db';
|
|
1184
|
+
|
|
1185
|
+
const db = await initializeDB();
|
|
1186
|
+
console.log('Database initialized');
|
|
1187
|
+
|
|
1188
|
+
// userController.ts - Access anywhere
|
|
1189
|
+
import { getSmartDB } from '@bhushanpawar/sqldb';
|
|
1190
|
+
|
|
1191
|
+
export const getUsers = async () => {
|
|
1192
|
+
const db = getSmartDB(); // Returns the same instance
|
|
1193
|
+
return await (db as any).users.findMany();
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
// orderController.ts - Access anywhere
|
|
1197
|
+
import { getSmartDB } from '@bhushanpawar/sqldb';
|
|
1198
|
+
|
|
1199
|
+
export const getOrders = async (userId: number) => {
|
|
1200
|
+
const db = getSmartDB(); // Same instance
|
|
1201
|
+
return await (db as any).orders.findMany({ user_id: userId });
|
|
1202
|
+
};
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
**Benefits:**
|
|
1206
|
+
- Single connection pool shared across app
|
|
1207
|
+
- No need to pass `db` around
|
|
1208
|
+
- Prevents multiple connections
|
|
1209
|
+
- Clean architecture
|
|
1210
|
+
|
|
1211
|
+
**See:** [singleton-example.ts](./examples/singleton-example.ts), [SINGLETON_PATTERN.md](./docs/SINGLETON_PATTERN.md)
|
|
1212
|
+
|
|
1213
|
+
---
|
|
1214
|
+
|
|
1215
|
+
### 8. Cache Warming for Better Performance
|
|
1216
|
+
|
|
1217
|
+
Pre-warm cache on startup for frequently accessed queries:
|
|
1218
|
+
|
|
1219
|
+
```typescript
|
|
1220
|
+
import { createSmartDB } from '@bhushanpawar/sqldb';
|
|
1221
|
+
|
|
1222
|
+
const db = await createSmartDB({
|
|
1223
|
+
mariadb: { /* config */ },
|
|
1224
|
+
redis: { /* config */ },
|
|
1225
|
+
cache: { enabled: true },
|
|
1226
|
+
});
|
|
1227
|
+
|
|
1228
|
+
// Warm cache for specific queries
|
|
1229
|
+
await (db as any).users.warmCache({ status: 'active' });
|
|
1230
|
+
await (db as any).products.warmCache({ featured: true });
|
|
1231
|
+
|
|
1232
|
+
// Warm cache with related tables (follows foreign keys)
|
|
1233
|
+
await (db as any).orders.warmCacheWithRelations(
|
|
1234
|
+
{ status: 'pending' },
|
|
1235
|
+
{
|
|
1236
|
+
depth: 1, // How deep to traverse relationships
|
|
1237
|
+
warmDependents: true, // Warm tables that reference this table
|
|
1238
|
+
warmDependencies: true, // Warm tables this table references
|
|
1239
|
+
correlationId: 'startup-warming',
|
|
1240
|
+
}
|
|
1241
|
+
);
|
|
1242
|
+
|
|
1243
|
+
// This warms:
|
|
1244
|
+
// - orders (main table)
|
|
1245
|
+
// - users (orders.user_id → users.id)
|
|
1246
|
+
// - order_items (order_items.order_id → orders.id)
|
|
1247
|
+
// - products (order_items.product_id → products.id)
|
|
1248
|
+
|
|
1249
|
+
// Now these queries are instant (served from cache)
|
|
1250
|
+
const orders = await (db as any).orders.findMany({ status: 'pending' });
|
|
1251
|
+
const user = await (db as any).users.findById(orders[0].user_id);
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
**Use cases:**
|
|
1255
|
+
- Application startup optimization
|
|
1256
|
+
- Pre-loading frequently accessed data
|
|
1257
|
+
- Improving first request performance
|
|
1258
|
+
|
|
1259
|
+
**See:** Cache warming section above
|
|
1260
|
+
|
|
1261
|
+
---
|
|
1262
|
+
|
|
1263
|
+
### 9. Auto-Warming - Intelligent Background Cache Warming
|
|
1264
|
+
|
|
1265
|
+
Automatically warm cache for your hottest queries:
|
|
1266
|
+
|
|
1267
|
+
```typescript
|
|
1268
|
+
import { createSmartDB } from '@bhushanpawar/sqldb';
|
|
1269
|
+
|
|
1270
|
+
const db = await createSmartDB({
|
|
1271
|
+
mariadb: { /* config */ },
|
|
1272
|
+
redis: { /* config */ },
|
|
1273
|
+
cache: { enabled: true },
|
|
1274
|
+
warming: {
|
|
1275
|
+
enabled: true, // Enable auto-warming
|
|
1276
|
+
intervalMs: 60000, // Warm every 60 seconds
|
|
1277
|
+
topQueriesPerTable: 10, // Warm top 10 queries per table
|
|
1278
|
+
minAccessCount: 3, // Must be accessed at least 3 times
|
|
1279
|
+
maxStatsAge: 3600000, // Consider queries from last hour
|
|
1280
|
+
useSeperatePool: true, // Use separate connection pool
|
|
1281
|
+
warmingPoolSize: 2, // 2 connections for warming
|
|
1282
|
+
trackInDatabase: true, // Persist stats in database
|
|
1283
|
+
statsTableName: '__sqldb_query_stats',
|
|
1284
|
+
|
|
1285
|
+
// Callbacks
|
|
1286
|
+
onWarmingComplete: (stats) => {
|
|
1287
|
+
console.log('Warming complete:', {
|
|
1288
|
+
queriesWarmed: stats.queriesWarmed,
|
|
1289
|
+
cacheHitRateBefore: (stats.cacheHitRateBefore * 100).toFixed(1) + '%',
|
|
1290
|
+
cacheHitRateAfter: (stats.cacheHitRateAfter * 100).toFixed(1) + '%',
|
|
1291
|
+
});
|
|
1292
|
+
},
|
|
1293
|
+
onWarmingError: (error) => {
|
|
1294
|
+
console.error('Warming error:', error.message);
|
|
1295
|
+
},
|
|
1296
|
+
},
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
// Use your app normally - auto-warming tracks which queries are hot
|
|
1300
|
+
for (let i = 0; i < 10; i++) {
|
|
1301
|
+
const users = await (db as any).users.findMany({ status: 'active' });
|
|
1302
|
+
const orders = await (db as any).orders.findMany({ status: 'pending' });
|
|
1303
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// After 60 seconds, auto-warming will:
|
|
1307
|
+
// 1. Identify the most frequently accessed queries
|
|
1308
|
+
// 2. Pre-warm them in the background
|
|
1309
|
+
// 3. Improve cache hit rate automatically
|
|
1310
|
+
|
|
1311
|
+
// Check warming stats
|
|
1312
|
+
const warmingStats = db.getWarmingStats();
|
|
1313
|
+
console.log('Latest warming:', {
|
|
1314
|
+
queriesWarmed: warmingStats.queriesWarmed,
|
|
1315
|
+
totalTime: warmingStats.totalTimeMs + 'ms',
|
|
1316
|
+
perTable: warmingStats.tables,
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
// Manually trigger warming
|
|
1320
|
+
const manualStats = await db.warmCache();
|
|
1321
|
+
console.log('Manual warming:', manualStats.queriesWarmed, 'queries');
|
|
1322
|
+
```
|
|
1323
|
+
|
|
1324
|
+
**How it works:**
|
|
1325
|
+
1. Tracks query frequency per table in `__sqldb_query_stats` table
|
|
1326
|
+
2. Every X seconds, identifies the hottest queries
|
|
1327
|
+
3. Pre-warms them using a separate connection pool (no impact on app)
|
|
1328
|
+
4. Persists stats across restarts
|
|
1329
|
+
|
|
1330
|
+
**Benefits:**
|
|
1331
|
+
- Automatic - no manual configuration
|
|
1332
|
+
- Intelligent - only warms frequently used queries
|
|
1333
|
+
- Non-blocking - uses separate connection pool
|
|
1334
|
+
- Persistent - stats survive app restarts
|
|
1335
|
+
- Observable - callbacks for monitoring
|
|
1336
|
+
|
|
1337
|
+
**See:** [auto-warming-example.ts](./examples/auto-warming-example.ts), [AUTO_WARMING.md](./docs/AUTO_WARMING.md)
|
|
1338
|
+
|
|
1339
|
+
---
|
|
1340
|
+
|
|
1341
|
+
### 10. Complete Production Example
|
|
1342
|
+
|
|
1343
|
+
A real-world production setup with all features:
|
|
1344
|
+
|
|
1345
|
+
```typescript
|
|
1346
|
+
import { createSmartDB, generateQueryId } from '@bhushanpawar/sqldb';
|
|
1347
|
+
|
|
1348
|
+
// Production configuration
|
|
1349
|
+
const db = await createSmartDB({
|
|
1350
|
+
mariadb: {
|
|
1351
|
+
host: process.env.DB_HOST,
|
|
1352
|
+
port: parseInt(process.env.DB_PORT || '3306'),
|
|
1353
|
+
user: process.env.DB_USER,
|
|
1354
|
+
password: process.env.DB_PASSWORD,
|
|
1355
|
+
database: process.env.DB_NAME,
|
|
1356
|
+
connectionLimit: 20,
|
|
1357
|
+
acquireTimeout: 10000,
|
|
1358
|
+
connectTimeout: 10000,
|
|
1359
|
+
logging: process.env.NODE_ENV === 'development',
|
|
1360
|
+
},
|
|
1361
|
+
redis: {
|
|
1362
|
+
host: process.env.REDIS_HOST,
|
|
1363
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
1364
|
+
password: process.env.REDIS_PASSWORD,
|
|
1365
|
+
keyPrefix: 'myapp:',
|
|
1366
|
+
},
|
|
1367
|
+
cache: {
|
|
1368
|
+
enabled: true,
|
|
1369
|
+
defaultTTL: 300, // 5 minutes
|
|
1370
|
+
maxKeys: 10000,
|
|
1371
|
+
invalidateOnWrite: true,
|
|
1372
|
+
cascadeInvalidation: true,
|
|
1373
|
+
},
|
|
1374
|
+
discovery: {
|
|
1375
|
+
autoDiscover: true,
|
|
1376
|
+
excludedTables: ['migrations', 'temp_*'],
|
|
1377
|
+
maxGraphDepth: 3,
|
|
1378
|
+
refreshInterval: 3600000, // Refresh schema every hour
|
|
1379
|
+
},
|
|
1380
|
+
warming: {
|
|
1381
|
+
enabled: process.env.NODE_ENV === 'production',
|
|
1382
|
+
intervalMs: 300000, // Warm every 5 minutes
|
|
1383
|
+
topQueriesPerTable: 20,
|
|
1384
|
+
minAccessCount: 5,
|
|
1385
|
+
useSeperatePool: true,
|
|
1386
|
+
trackInDatabase: true,
|
|
1387
|
+
onWarmingComplete: (stats) => {
|
|
1388
|
+
logger.info('Cache warming complete', {
|
|
1389
|
+
queriesWarmed: stats.queriesWarmed,
|
|
1390
|
+
hitRateImprovement:
|
|
1391
|
+
((stats.cacheHitRateAfter - stats.cacheHitRateBefore) * 100).toFixed(2) + '%',
|
|
1392
|
+
});
|
|
1393
|
+
},
|
|
1394
|
+
},
|
|
1395
|
+
logging: {
|
|
1396
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
1397
|
+
logger: (level, message, meta) => {
|
|
1398
|
+
// Use your preferred logger (Winston, Pino, etc.)
|
|
1399
|
+
logger[level](message, meta);
|
|
1400
|
+
},
|
|
1401
|
+
},
|
|
1402
|
+
}, { singleton: true });
|
|
1403
|
+
|
|
1404
|
+
// Express middleware for request tracking
|
|
1405
|
+
app.use((req, res, next) => {
|
|
1406
|
+
req.correlationId = generateQueryId();
|
|
1407
|
+
res.on('finish', () => {
|
|
1408
|
+
const queries = db.getQueries(req.correlationId);
|
|
1409
|
+
const totalTime = queries.reduce((sum, q) => sum + (q.executionTimeMs || 0), 0);
|
|
1410
|
+
|
|
1411
|
+
// Log slow requests
|
|
1412
|
+
if (totalTime > 1000) {
|
|
1413
|
+
logger.warn('Slow request', {
|
|
1414
|
+
path: req.path,
|
|
1415
|
+
method: req.method,
|
|
1416
|
+
totalTime,
|
|
1417
|
+
queryCount: queries.length,
|
|
1418
|
+
correlationId: req.correlationId,
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
db.clearQueries(req.correlationId);
|
|
1423
|
+
});
|
|
1424
|
+
next();
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
// Health check endpoint
|
|
1428
|
+
app.get('/health', async (req, res) => {
|
|
1429
|
+
const health = await db.healthCheck();
|
|
1430
|
+
const stats = db.getCacheManager().getStats();
|
|
1431
|
+
|
|
1432
|
+
res.json({
|
|
1433
|
+
status: health.mariadb && health.redis ? 'healthy' : 'unhealthy',
|
|
1434
|
+
...health,
|
|
1435
|
+
cache: stats,
|
|
1436
|
+
timestamp: new Date().toISOString(),
|
|
1437
|
+
});
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
// Graceful shutdown
|
|
1441
|
+
process.on('SIGTERM', async () => {
|
|
1442
|
+
logger.info('SIGTERM received, closing connections...');
|
|
1443
|
+
await db.close();
|
|
1444
|
+
process.exit(0);
|
|
1445
|
+
});
|
|
1446
|
+
```
|
|
1447
|
+
|
|
1448
|
+
**Production best practices:**
|
|
1449
|
+
- Environment-based configuration
|
|
1450
|
+
- Connection pooling optimization
|
|
1451
|
+
- Schema refresh scheduling
|
|
1452
|
+
- Auto-warming in production only
|
|
1453
|
+
- Request tracking middleware
|
|
1454
|
+
- Performance monitoring
|
|
1455
|
+
- Health checks
|
|
1456
|
+
- Graceful shutdown
|
|
1457
|
+
|
|
1458
|
+
---
|
|
1459
|
+
|
|
1460
|
+
### More Examples
|
|
1461
|
+
|
|
1462
|
+
For complete working examples, see the [examples](./examples) directory:
|
|
777
1463
|
|
|
778
|
-
- [usage.ts](./examples/usage.ts) - Basic CRUD operations
|
|
1464
|
+
- [basic-usage.ts](./examples/basic-usage.ts) - Basic CRUD operations
|
|
1465
|
+
- [typed-tables-example.ts](./examples/typed-tables-example.ts) - TypeScript type safety
|
|
779
1466
|
- [query-tracking.ts](./examples/query-tracking.ts) - Query tracking with correlation IDs
|
|
780
|
-
- [
|
|
1467
|
+
- [query-logging-example.ts](./examples/query-logging-example.ts) - Enhanced query logging
|
|
1468
|
+
- [relationships-example.ts](./examples/relationships-example.ts) - Smart cache invalidation
|
|
1469
|
+
- [singleton-example.ts](./examples/singleton-example.ts) - Singleton pattern
|
|
1470
|
+
- [auto-warming-example.ts](./examples/auto-warming-example.ts) - Auto-warming system
|
|
1471
|
+
- [hooks-example.ts](./examples/hooks-example.ts) - Custom hooks and extensibility
|
|
781
1472
|
|
|
782
1473
|
## Documentation
|
|
783
1474
|
|