sqlcache 2.9.0 → 2.9.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.
@@ -1,23 +1,25 @@
1
1
  # Configuration Guide
2
2
 
3
- Complete configuration reference for MySQL Redis Cache Connector.
3
+ Complete configuration reference for sqlcache.
4
4
 
5
5
  ---
6
6
 
7
7
  ## Environment Variables
8
8
 
9
+ sqlcache reads its settings from `process.env` when it is first required. It loads `.env` from the
10
+ working directory (only that file, and `${VAR}` references are not expanded); variables already set
11
+ in the environment take precedence. A commented template is in `node_modules/sqlcache/.env.example`.
12
+
9
13
  ### Required Configuration
10
14
 
11
15
  ```bash
12
16
  # MySQL Database
13
17
  DB_HOST=localhost # MySQL host
14
18
  DB_USERNAME=root # MySQL username
15
- DB_PASSWORD=secret # MySQL password
16
19
  DB_NAME=mydb # Database name
17
20
 
18
- # Redis Cache
21
+ # Redis Cache (required unless REDIS_ENABLED=false)
19
22
  REDIS_SERVER=localhost # Redis host
20
- REDIS_PORT=6379 # Redis port
21
23
  ```
22
24
 
23
25
  ### Optional Configuration
@@ -25,23 +27,33 @@ REDIS_PORT=6379 # Redis port
25
27
  #### Database Settings
26
28
 
27
29
  ```bash
30
+ DB_PASSWORD=secret # MySQL password (default: empty)
28
31
  DB_PORT=3306 # MySQL port (default: 3306)
29
- DB_CONNECTION_LIMIT=10 # Max connections in pool (default: 151)
32
+ DB_CONNECTION_LIMIT=10 # Max connections in pool, per process (default: 151)
30
33
  DB_QUEUE_LIMIT=0 # Max queued requests (default: 0 = unlimited)
31
34
  DB_CONNECT_TIMEOUT=10000 # Connection timeout in ms (default: 10000)
32
- DB_MULTIPLE_STATEMENTS=false # Allow multiple statements (default: true, for backward compatibility)
35
+ DB_MULTIPLE_STATEMENTS=false # Allow multiple statements (default: true, for backward compatibility; false is recommended for new projects)
33
36
  DB_COALESCE_QUERIES=true # Concurrent identical cache misses share one query (default: true)
37
+ DB_COALESCE_MAX_WAIT=10000 # Max wait (ms) for such a shared query before running a fresh one (default: 10000, v2.9.1)
34
38
  DB_PAGINATION_REUSE_TOTAL=false # Reuse a query's pagination total across pages (default: false)
35
39
  DB_MAX_IDLE=10 # Idle connections kept in the pool (default: 10)
36
40
  DB_IDLE_TIMEOUT=60000 # Idle time (ms) before connections above DB_MAX_IDLE close (default: 60000)
37
41
  TIMEZONE=+00:00 # Timezone offset (default: +00:00)
38
42
  ```
39
43
 
44
+ `DB_MULTIPLE_STATEMENTS=true` lets an SQL injection run additional statements; new projects should
45
+ set it to `false` unless they send several statements in one query. The default stays `true` in 2.x.
46
+
47
+ Size the pool for the whole deployment: instances × workers × `DB_CONNECTION_LIMIT`, plus any other
48
+ clients, must stay below MySQL's `max_connections`. MySQL's default `max_connections` is 151, the same
49
+ as sqlcache's default limit, so a second process with default settings can already exceed it.
50
+
40
51
  #### Redis Settings
41
52
 
42
53
  ```bash
54
+ REDIS_PORT=6379 # Redis port (default: 6379)
43
55
  REDIS_PASSWORD=secret # Redis password (if required)
44
- REDIS_VHOST=my_app # Redis key namespace/prefix
56
+ REDIS_VHOST=my_app # Redis key namespace/prefix, keys become my_app:<key> (set once per process)
45
57
  REDIS_ENABLED=true # Enable/disable Redis (default: true)
46
58
  ```
47
59
 
@@ -61,18 +73,25 @@ replayed once it is ready again. See [MIGRATION.md](MIGRATION.md#v28x--v290-perf
61
73
  for the full list of v2.9.0 changes and how to restore v2.8.x behavior.
62
74
 
63
75
  With `REDIS_ENABLED=false` no Redis connection is opened unless a Redis function
64
- (`getArrayItem`, `getRedisClient`, ...) is called directly.
76
+ (`getArrayItem`, `getRedisClient`, ...) is called directly. Since v2.9.1 `isRedisConnected()` then
77
+ returns `false` without opening one.
78
+
79
+ `REDIS_VHOST` applies to the whole process. Changing it at runtime (for example per tenant request)
80
+ mixes the cache entries of concurrent requests; v2.9.1 logs a one-time warning when it changes.
81
+
82
+ Redis is a trusted store: cached rows are returned as stored. Protect it with AUTH
83
+ (`REDIS_PASSWORD`) and a private network, and give each application its own `REDIS_VHOST` or Redis
84
+ database.
65
85
 
66
86
  #### Smart Features (v2.6.0)
67
87
 
68
88
  ```bash
69
- # Enable all smart features
70
- CORE_AUTO_FEATURES=true # Auto key + auto invalidation
71
-
72
- # Or enable individually
73
- CORE_AUTO_INVALIDATION=true # Auto cache invalidation only
89
+ CORE_AUTO_FEATURES=true # Auto cache keys only (does not enable invalidation)
90
+ CORE_AUTO_INVALIDATION=true # Auto cache invalidation
74
91
  ```
75
92
 
93
+ Set both to use automatic keys together with automatic invalidation.
94
+
76
95
  ---
77
96
 
78
97
  ## Programmatic Configuration
@@ -89,14 +108,17 @@ configure({
89
108
  autoInvalidation: {
90
109
  enabled: true,
91
110
  tables: {
92
- users: ['users_*', 'profiles_*', 'sessions_*'],
93
- orders: ['orders_*', 'cart_*', 'analytics_*'],
94
- products: ['products_*', 'catalog_*']
111
+ users: ['users:*', 'users_*', 'profiles_*', 'sessions_*'],
112
+ orders: ['orders:*', 'orders_*', 'cart_*', 'analytics_*'],
113
+ products: ['products:*', 'products_*', 'catalog_*']
95
114
  }
96
115
  }
97
116
  });
98
117
  ```
99
118
 
119
+ A `tables` rule replaces the default `<table>_*` and `<table>:*` patterns for that table. Automatic
120
+ keys start with `<table>:`, so keep `<table>:*` in the rule when you use them.
121
+
100
122
  ### Individual Feature Configuration
101
123
 
102
124
  ```javascript
@@ -109,16 +131,36 @@ enableAutoKey({ enabled: true });
109
131
  enableAutoInvalidation({
110
132
  enabled: true,
111
133
  tables: {
112
- users: 'users_*',
113
- orders: ['orders_*', 'analytics_*']
134
+ users: ['users:*', 'users_*'],
135
+ orders: ['orders:*', 'orders_*', 'analytics_*']
114
136
  }
115
137
  });
138
+
139
+ // Turn features off again (`false` works since v2.9.1; earlier versions turned the feature on for it)
140
+ enableAutoKey(false);
141
+ enableAutoInvalidation({ enabled: false });
116
142
  ```
117
143
 
144
+ `enableAutoKey`, `enableAutoInvalidation` and the `autoKey` / `autoInvalidation` options of
145
+ `configure` accept `true`/`false` as well as an options object.
146
+
118
147
  ---
119
148
 
120
149
  ## Configuration by Environment
121
150
 
151
+ sqlcache itself only loads `.env`. To keep one file per environment, load it yourself before the
152
+ first `require('sqlcache')` (with `dotenv` in your own dependencies); values that are already set
153
+ are not overwritten by `.env`:
154
+
155
+ ```javascript
156
+ require('dotenv').config({ path: `.env.${process.env.NODE_ENV}` });
157
+ const { getCacheQuery } = require('sqlcache');
158
+ ```
159
+
160
+ Secrets such as `DB_PASSWORD` and `REDIS_PASSWORD` are best set in the process environment by your
161
+ deployment. `${VAR}` references in these files are not expanded: `DB_PASSWORD=${PROD_DB_PASSWORD}`
162
+ would be used literally.
163
+
122
164
  ### Development
123
165
 
124
166
  ```bash
@@ -131,44 +173,48 @@ DB_CONNECTION_LIMIT=5
131
173
  REDIS_SERVER=localhost
132
174
  REDIS_PORT=6379
133
175
  CORE_AUTO_FEATURES=true
176
+ CORE_AUTO_INVALIDATION=true
134
177
  ```
135
178
 
136
179
  ### Staging
137
180
 
138
181
  ```bash
139
- # .env.staging
182
+ # .env.staging (DB_PASSWORD and REDIS_PASSWORD come from the environment)
140
183
  DB_HOST=staging-db.example.com
141
184
  DB_USERNAME=app_user
142
- DB_PASSWORD=${STAGING_DB_PASSWORD}
143
185
  DB_NAME=myapp_staging
144
186
  DB_CONNECTION_LIMIT=20
187
+ DB_MULTIPLE_STATEMENTS=false
145
188
  REDIS_SERVER=staging-redis.example.com
146
189
  REDIS_PORT=6379
147
- REDIS_PASSWORD=${STAGING_REDIS_PASSWORD}
148
190
  CORE_AUTO_FEATURES=true
191
+ CORE_AUTO_INVALIDATION=true
149
192
  ```
150
193
 
151
194
  ### Production
152
195
 
153
196
  ```bash
154
- # .env.production
197
+ # .env.production (DB_PASSWORD and REDIS_PASSWORD come from the environment)
155
198
  DB_HOST=prod-db.example.com
156
199
  DB_USERNAME=app_user
157
- DB_PASSWORD=${PROD_DB_PASSWORD}
158
200
  DB_NAME=myapp_prod
159
201
  DB_CONNECTION_LIMIT=50
160
202
  DB_CONNECT_TIMEOUT=5000
203
+ DB_MULTIPLE_STATEMENTS=false
161
204
  REDIS_SERVER=prod-redis.example.com
162
205
  REDIS_PORT=6379
163
- REDIS_PASSWORD=${PROD_REDIS_PASSWORD}
164
206
  REDIS_VHOST=myapp_prod
165
207
  CORE_AUTO_FEATURES=true
208
+ CORE_AUTO_INVALIDATION=true
166
209
  ```
167
210
 
168
211
  ---
169
212
 
170
213
  ## Connection Pool Tuning
171
214
 
215
+ The limits below are per process. Multiply by the number of instances and workers, add other
216
+ clients, and keep the sum below MySQL's `max_connections` (default 151).
217
+
172
218
  ### Small Application (< 100 concurrent users)
173
219
 
174
220
  ```bash
@@ -207,8 +253,10 @@ DB_MAX_IDLE=50
207
253
 
208
254
  ### Large Result Sets and Deep Pages
209
255
 
210
- - Cached results are stored as JSON: about 10 ms to serialize and 4 ms to parse per 10,000 rows of
211
- a typical 10-column table, so prefer `getCacheQueryPagination` over caching very large results.
256
+ - Cached results are stored as JSON, and serializing and parsing grow with the number of rows (see
257
+ the README's Performance section), so prefer `getCacheQueryPagination` over caching very large
258
+ results. On a cache hit a `Date` is an ISO string and a `Buffer` is `{ type, data }`, while a miss
259
+ returns mysql2's types; normalize such columns in your code.
212
260
  - Pagination uses `LIMIT offset, size`; MySQL reads and skips `offset` rows, so very deep pages cost
213
261
  more. For long lists, an indexed `WHERE id > ?` (keyset) condition in the SQL stays fast at any depth.
214
262
  - `DB_PAGINATION_REUSE_TOTAL=true` avoids recounting the total on every page miss of large lists.
@@ -224,12 +272,13 @@ setInterval(() => {
224
272
  total: stats.totalConnections,
225
273
  active: stats.activeConnections,
226
274
  free: stats.freeConnections,
227
- queued: stats.queuedRequests,
228
- utilization: (stats.activeConnections / stats.totalConnections * 100).toFixed(2) + '%'
275
+ queued: stats.queuedRequests
229
276
  });
230
277
 
231
- if (stats.activeConnections === stats.totalConnections) {
232
- console.warn('⚠️ Connection pool exhausted!');
278
+ // Connections are opened on demand, so an idle pool has 0 of 0 connections in use.
279
+ // Requests waiting for a connection are what shows an exhausted pool.
280
+ if (stats.queuedRequests > 0) {
281
+ console.warn('⚠️ Connection pool exhausted: requests are waiting for a connection');
233
282
  }
234
283
  }, 60000); // Check every minute
235
284
  ```
@@ -267,19 +316,26 @@ When auto-invalidation is enabled, default patterns are:
267
316
  // - Clears: users:*
268
317
  ```
269
318
 
319
+ They apply to `QuaryCache` and `tx.query` writes without a `resetCacheName`, and since v2.9.1 also to
320
+ `bulkInsert` without one. A `resetCacheName` replaces the automatic patterns for that call.
321
+
270
322
  #### Custom Patterns
271
323
 
324
+ A rule replaces the defaults for its table, so keep `{table}:*` when you use automatic keys:
325
+
272
326
  ```javascript
273
327
  enableAutoInvalidation({
274
328
  enabled: true,
275
329
  tables: {
276
330
  users: [
331
+ 'users:*', // Automatic keys
277
332
  'users_*', // All user caches
278
333
  'profiles_*', // User profiles
279
334
  'sessions_*', // User sessions
280
335
  'feed:*' // User feeds
281
336
  ],
282
337
  orders: [
338
+ 'orders:*',
283
339
  'orders_*',
284
340
  'cart_*',
285
341
  'analytics_orders_*'
@@ -288,6 +344,9 @@ enableAutoInvalidation({
288
344
  });
289
345
  ```
290
346
 
347
+ Patterns are Redis glob patterns used as prefixes: escape `*`, `?`, `[`, `]` and `\` in any part that
348
+ comes from user input. An empty prefix without `REDIS_VHOST` deletes every key in that Redis database.
349
+
291
350
  ### Disable Redis
292
351
 
293
352
  ```bash
@@ -296,14 +355,17 @@ REDIS_ENABLED=false
296
355
 
297
356
  ```javascript
298
357
  // Queries execute normally without caching
299
- const users = await getCacheQuery('SELECT * FROM users', []);
300
- // No Redis involved
358
+ const users = await getCacheQuery('SELECT * FROM users', [], 'users:all');
359
+ // No Redis involved (a cache name is still required unless automatic keys are enabled)
301
360
  ```
302
361
 
303
362
  ---
304
363
 
305
364
  ## Auto Key Generation Strategy
306
365
 
366
+ Automatic keys are used when a call has no cache name and `CORE_AUTO_FEATURES=true` (or
367
+ `enableAutoKey()`) is set.
368
+
307
369
  ### Detailed Strategy (1-3 parameters)
308
370
 
309
371
  ```javascript
@@ -324,6 +386,20 @@ SELECT * FROM orders WHERE a = ? AND b = ? AND c = ? AND d = ?
324
386
  orders:9f2ac12b
325
387
  ```
326
388
 
389
+ ### Other Keys
390
+
391
+ - No `WHERE` clause: `<table>:all`, whatever the rest of the SQL.
392
+ - No detectable table (`CALL ...`, `SELECT fn(?)`): `query:<sqlhash>:<paramhash>` since v2.9.1
393
+ (earlier versions left the parameters out).
394
+ - Calls with a `database` argument: the key ends with `@<database>`.
395
+
396
+ ### Limits
397
+
398
+ The select list, `ORDER BY`, `LIMIT`, literal values and joined tables are not part of the key, and
399
+ the parameter hash is 32-bit, so different queries can share a key. Use explicit cache names for
400
+ aggregates, joins, `LIMIT`/`ORDER BY` variants, time-relative SQL (`NOW()`, `CURDATE()`) and anything
401
+ user-specific or sensitive.
402
+
327
403
  ### Manual Override
328
404
 
329
405
  ```javascript
@@ -344,11 +420,11 @@ const users = await getCacheQuery('SELECT * FROM users WHERE id = ?', [123], 'my
344
420
  # NEVER commit passwords to git
345
421
  # Use environment variables or secrets management
346
422
 
347
- # Good
348
- DB_PASSWORD=${DATABASE_PASSWORD}
349
- REDIS_PASSWORD=${REDIS_AUTH_TOKEN}
423
+ # Good: set DB_PASSWORD and REDIS_PASSWORD in the process environment (e.g. container secrets);
424
+ # they take precedence over .env. A line like DB_PASSWORD=${DATABASE_PASSWORD} in .env is
425
+ # NOT expanded and would be used literally.
350
426
 
351
- # Bad
427
+ # Bad: a real password in a committed .env file
352
428
  DB_PASSWORD=mypassword123
353
429
  ```
354
430
 
@@ -356,12 +432,30 @@ DB_PASSWORD=mypassword123
356
432
 
357
433
  ```javascript
358
434
  // ✅ Good - Use parameterized queries
359
- await getCacheQuery('SELECT * FROM users WHERE id = ?', [userId]);
435
+ await getCacheQuery('SELECT * FROM users WHERE id = ?', [userId], `user:${userId}`);
360
436
 
361
437
  // ❌ Bad - String interpolation
362
- await getCacheQuery(`SELECT * FROM users WHERE id = ${userId}`);
438
+ await getCacheQuery(`SELECT * FROM users WHERE id = ${userId}`, [], `user:${userId}`);
363
439
  ```
364
440
 
441
+ - Set `DB_MULTIPLE_STATEMENTS=false` in new projects, so an injection cannot run extra statements.
442
+ - `bulkInsert` puts the table name and the keys of the first record into the SQL as-is (v2.9.1
443
+ rejects invalid names before anything is sent). Map request or CSV input onto a fixed list of
444
+ columns; never pass untrusted keys.
445
+ - Cache-name prefixes are Redis glob patterns: escape `*`, `?`, `[`, `]` and `\` in user input.
446
+
447
+ ### Redis
448
+
449
+ - Redis is a trusted store: cached rows are returned as stored. Use AUTH, a private network, and a
450
+ `REDIS_VHOST` or Redis database of the application's own.
451
+ - `REDIS_VHOST` is per process. Do not change it per request; for tenants, pass the tenant's
452
+ `database` per call and put the tenant in explicit cache names.
453
+
454
+ ### Error Details
455
+
456
+ mysql2 errors carry `err.sql` with the parameter values filled in. Do not send whole error objects to
457
+ logs or clients you do not trust.
458
+
365
459
  ### Connection Limits
366
460
 
367
461
  ```bash
@@ -385,27 +479,37 @@ mysql -u root -p
385
479
  redis-cli ping
386
480
  ```
387
481
 
388
- ### Pool Exhaustion
482
+ ### Too Many Connections
389
483
 
390
484
  ```bash
391
- # Error: Too many connections
392
- # Solution: Increase connection limit
485
+ # Error: Too many connections (ER_CON_COUNT_ERROR)
486
+ # Cause: all clients together exceed MySQL's max_connections (default 151)
487
+ # Solution: lower DB_CONNECTION_LIMIT so that instances x workers x limit + other clients
488
+ # stays below max_connections, or raise max_connections on the server
393
489
 
394
- DB_CONNECTION_LIMIT=100
490
+ DB_CONNECTION_LIMIT=30
395
491
  ```
396
492
 
493
+ When the pool itself is exhausted, requests wait for a free connection (`queuedRequests` in
494
+ `getPoolStats()`), or fail once more than `DB_QUEUE_LIMIT` are waiting.
495
+
397
496
  ### Timeout Issues
398
497
 
399
498
  ```bash
400
499
  # Error: Query timeout exceeded
401
- # Solution: Increase timeout or optimize query
500
+ # Cause: a getCacheQueryWithTimeout() call took longer than its timeout option (default 30000 ms)
501
+ # Solution: pass a larger timeout, or optimize the query; DB_CONNECT_TIMEOUT does not affect it
502
+ ```
402
503
 
403
- DB_CONNECT_TIMEOUT=30000
504
+ ```javascript
505
+ await getCacheQueryWithTimeout(sql, params, 'report:daily', { timeout: 60000 });
404
506
  ```
405
507
 
406
508
  ### Redis Outage Log Lines
407
509
 
408
510
  ```bash
511
+ # "Redis reconnecting... (attempt 3, delay 2000ms)"
512
+ # One line per reconnect attempt (v2.9.1) while Redis is unreachable.
409
513
  # "Redis operation failed: Redis unavailable for 10234ms — serving cache misses without waiting until it recovers"
410
514
  # Redis has been unreachable for REDIS_WAIT_TIMEOUT: cache reads/writes fall back immediately
411
515
  # (once per outage). Restore the old wait-on-every-call behavior with REDIS_FAIL_FAST=false.
@@ -417,14 +521,18 @@ DB_CONNECT_TIMEOUT=30000
417
521
  # Deletes that failed during the outage are being applied now that Redis is back.
418
522
  ```
419
523
 
524
+ Since v2.9.1 a line is also logged when more than 1000 invalidations are missed during an outage
525
+ (only the most recent 1000 are replayed), and a one-time warning when `REDIS_VHOST` changes at
526
+ runtime.
527
+
420
528
  ### Cache Issues
421
529
 
422
530
  ```bash
423
- # Clear all cache
531
+ # Clear all cache (FLUSHALL deletes every key on that Redis server, not only sqlcache's)
424
532
  redis-cli FLUSHALL
425
533
 
426
- # Clear specific pattern
427
- redis-cli --scan --pattern "users_*" | xargs redis-cli DEL
534
+ # Clear specific pattern (with REDIS_VHOST=my_app the keys start with "my_app:")
535
+ redis-cli --scan --pattern "my_app:users_*" | xargs redis-cli DEL
428
536
  ```
429
537
 
430
538
  ---
@@ -1,4 +1,4 @@
1
- # Contributing to MySQL Redis Cache Connector
1
+ # Contributing to sqlcache
2
2
 
3
3
  Thank you for your interest in contributing! We welcome contributions from the community.
4
4
 
@@ -16,7 +16,7 @@ Thank you for your interest in contributing! We welcome contributions from the c
16
16
 
17
17
  ### Suggesting Features
18
18
 
19
- 1. Check existing issues and discussions
19
+ 1. Check existing issues
20
20
  2. Create a new issue with:
21
21
  - Clear use case description
22
22
  - Why this feature is needed
@@ -29,7 +29,7 @@ Thank you for your interest in contributing! We welcome contributions from the c
29
29
  ```bash
30
30
  git clone https://github.com/YOUR_USERNAME/sqlcache.git
31
31
  cd sqlcache
32
- npm install
32
+ npm ci
33
33
  ```
34
34
 
35
35
  2. **Create a feature branch**
@@ -47,6 +47,7 @@ Thank you for your interest in contributing! We welcome contributions from the c
47
47
  ```bash
48
48
  npm test
49
49
  npm run coverage
50
+ npx -p typescript@5 tsc -p test/types # type definitions check
50
51
  ```
51
52
 
52
53
  5. **Commit your changes**
@@ -68,9 +69,8 @@ Thank you for your interest in contributing! We welcome contributions from the c
68
69
 
69
70
  ### Prerequisites
70
71
 
71
- - Node.js 14+
72
- - MySQL 5.7+
73
- - Redis 3+
72
+ - Node.js 14+ for the package; the test suite runs in CI on Node 18, 20, 22, 24 and 26
73
+ - For integration tests: MySQL 5.7+ (CI uses 8.0) and Redis 2.8+ (CI uses 7), in Docker
74
74
 
75
75
  ### Local Setup
76
76
 
@@ -79,24 +79,24 @@ Thank you for your interest in contributing! We welcome contributions from the c
79
79
  git clone https://github.com/hayatialikeles/sqlcache.git
80
80
  cd sqlcache
81
81
 
82
- # Install dependencies
83
- npm install
82
+ # Install dependencies (node_modules is not committed)
83
+ npm ci
84
84
 
85
- # Copy environment file
86
- cp .env.example .env
87
-
88
- # Edit .env with your database credentials
89
- nano .env
90
-
91
- # Run tests
85
+ # Unit tests (no MySQL or Redis needed)
92
86
  npm test
93
87
 
94
88
  # Run with coverage
95
89
  npm run coverage
90
+
91
+ # Type definitions check, as run in CI
92
+ npx -p typescript@5 tsc -p test/types
96
93
  ```
97
94
 
98
95
  ### Environment Configuration
99
96
 
97
+ Unit and integration tests set the variables they need themselves. To try the package against your
98
+ own servers, copy `.env.example` to `.env` and edit it:
99
+
100
100
  ```bash
101
101
  # Database
102
102
  DB_HOST=localhost
@@ -111,7 +111,8 @@ REDIS_PORT=6379
111
111
  REDIS_PASSWORD=
112
112
 
113
113
  # Features
114
- CORE_AUTO_FEATURES=true
114
+ CORE_AUTO_FEATURES=true # automatic cache keys
115
+ CORE_AUTO_INVALIDATION=true # automatic invalidation
115
116
  ```
116
117
 
117
118
  ## ✅ Testing Guidelines
@@ -156,8 +157,20 @@ npm run coverage
156
157
 
157
158
  # Run specific test file
158
159
  npx mocha test/transaction.test.js
160
+
161
+ # Integration tests: Redis on 127.0.0.1:6399, MySQL on 127.0.0.1:3399 (root / testpass / testdb).
162
+ # Container commands use DOCKER_BIN (default: docker) and REDIS_CONTAINER (default: test-redis).
163
+ INTEGRATION=1 npm test
164
+
165
+ # Chaos suites (Docker containers test-redis / test-mysql)
166
+ INTEGRATION=1 node test/chaos.test.js
167
+ INTEGRATION=1 node test/chaos-mysql.test.js
159
168
  ```
160
169
 
170
+ CI (`.github/workflows/ci.yml`) runs the unit tests on Node 18, 20, 22, 24 and 26, once more against
171
+ the minimum dependency versions (mysql2 3.23.1, redis 3.1.2, dotenv 16.0.3), the type definitions
172
+ check, and the integration tests against MySQL 8.0 and Redis 7.
173
+
161
174
  ## 📚 Documentation Guidelines
162
175
 
163
176
  ### Code Comments
@@ -223,12 +236,10 @@ Follow [Conventional Commits](https://www.conventionalcommits.org/):
223
236
 
224
237
  ## 🐛 Debugging
225
238
 
226
- ### Enable Debug Logs
239
+ ### Logs
227
240
 
228
- ```javascript
229
- // Add to your code
230
- process.env.DEBUG = 'mysql-cache:*';
231
- ```
241
+ sqlcache has no debug switch. It logs connection events (Redis connects, reconnects and outages,
242
+ pool shutdown) with `console.log` / `console.error`.
232
243
 
233
244
  ### Common Issues
234
245
 
@@ -243,17 +254,17 @@ redis-cli ping
243
254
 
244
255
  **Test Failures**
245
256
  ```bash
246
- # Clear cache
247
- redis-cli FLUSHALL
257
+ # Clear the test Redis (integration tests use port 6399)
258
+ redis-cli -p 6399 FLUSHALL
248
259
 
249
- # Reset test database
250
- mysql -u root -p test_db < schema.sql
260
+ # The integration tests create and drop their own table (test_users) in testdb
251
261
  ```
252
262
 
253
263
  ## 📋 Checklist Before PR
254
264
 
255
265
  - [ ] All tests pass (`npm test`)
256
266
  - [ ] Coverage is >95% (`npm run coverage`)
267
+ - [ ] Type definitions check passes (`npx -p typescript@5 tsc -p test/types`)
257
268
  - [ ] Documentation updated
258
269
  - [ ] TypeScript definitions updated
259
270
  - [ ] Commit messages follow convention
@@ -265,7 +276,7 @@ mysql -u root -p test_db < schema.sql
265
276
  (For maintainers only)
266
277
 
267
278
  1. Update version in `package.json`
268
- 2. Update `CHANGELOG.md`
279
+ 2. Describe the changes in `docs/MIGRATION.md`
269
280
  3. Run tests: `npm test`
270
281
  4. Commit: `git commit -m "chore: release v2.x.x"`
271
282
  5. Tag: `git tag v2.x.x`
@@ -274,9 +285,8 @@ mysql -u root -p test_db < schema.sql
274
285
 
275
286
  ## 💬 Communication
276
287
 
277
- - **Issues**: Bug reports and feature requests
278
- - **Discussions**: General questions and ideas
279
- - **Email**: For security issues only
288
+ - **Issues**: Bug reports, feature requests and questions
289
+ - **Security issues**: please do not open a public issue; contact the maintainer privately
280
290
 
281
291
  ## 📜 License
282
292