baja-lite 1.8.7 → 1.8.8
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/package.json +3 -6
- package/sql.d.ts +22 -7
- package/sql.js +423 -391
package/sql.js
CHANGED
|
@@ -238,20 +238,20 @@ class MysqlConnection {
|
|
|
238
238
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
239
239
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
240
240
|
}
|
|
241
|
-
return
|
|
241
|
+
return (async () => {
|
|
242
242
|
try {
|
|
243
243
|
const [_result] = await this[_daoConnection].execute(sql, params);
|
|
244
244
|
const result = _result;
|
|
245
245
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
246
246
|
globalThis[_LoggerService].verbose?.(result);
|
|
247
247
|
}
|
|
248
|
-
|
|
248
|
+
return { affectedRows: result.affectedRows, insertId: result.insertId };
|
|
249
249
|
}
|
|
250
250
|
catch (error) {
|
|
251
251
|
globalThis[_LoggerService].error(error.message, { cause: error });
|
|
252
|
-
|
|
252
|
+
throw error;
|
|
253
253
|
}
|
|
254
|
-
});
|
|
254
|
+
})();
|
|
255
255
|
}
|
|
256
256
|
pluck(sync, sql, params) {
|
|
257
257
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -267,16 +267,14 @@ class MysqlConnection {
|
|
|
267
267
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
268
268
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
269
269
|
}
|
|
270
|
-
return
|
|
270
|
+
return (async () => {
|
|
271
271
|
try {
|
|
272
272
|
const [result] = await this[_daoConnection].query(sql, params);
|
|
273
273
|
if (result && result[0]) {
|
|
274
274
|
const r = Object.values(result[0])[0];
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
resolve(null);
|
|
275
|
+
return r === null ? null : r;
|
|
279
276
|
}
|
|
277
|
+
return null;
|
|
280
278
|
}
|
|
281
279
|
catch (error) {
|
|
282
280
|
globalThis[_LoggerService].error(`
|
|
@@ -284,9 +282,9 @@ class MysqlConnection {
|
|
|
284
282
|
sql: ${sql},
|
|
285
283
|
params: ${params}
|
|
286
284
|
`);
|
|
287
|
-
|
|
285
|
+
throw error;
|
|
288
286
|
}
|
|
289
|
-
});
|
|
287
|
+
})();
|
|
290
288
|
}
|
|
291
289
|
get(sync, sql, params) {
|
|
292
290
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -302,15 +300,16 @@ class MysqlConnection {
|
|
|
302
300
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
303
301
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
304
302
|
}
|
|
305
|
-
return
|
|
303
|
+
return (async () => {
|
|
306
304
|
try {
|
|
307
305
|
const [result] = await this[_daoConnection].query(sql, params);
|
|
308
306
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
309
307
|
globalThis[_LoggerService].verbose?.(result);
|
|
310
308
|
}
|
|
311
|
-
if (result && result[0])
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
if (result && result[0]) {
|
|
310
|
+
return result[0];
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
314
313
|
}
|
|
315
314
|
catch (error) {
|
|
316
315
|
globalThis[_LoggerService].error(`
|
|
@@ -318,9 +317,9 @@ class MysqlConnection {
|
|
|
318
317
|
sql: ${sql},
|
|
319
318
|
params: ${params}
|
|
320
319
|
`);
|
|
321
|
-
|
|
320
|
+
throw error;
|
|
322
321
|
}
|
|
323
|
-
});
|
|
322
|
+
})();
|
|
324
323
|
}
|
|
325
324
|
raw(sync, sql, params) {
|
|
326
325
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -336,15 +335,18 @@ class MysqlConnection {
|
|
|
336
335
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
337
336
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
338
337
|
}
|
|
339
|
-
return
|
|
338
|
+
return (async () => {
|
|
340
339
|
try {
|
|
341
340
|
const [result] = await this[_daoConnection].query(sql, params);
|
|
342
341
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
343
342
|
globalThis[_LoggerService].verbose?.(result);
|
|
344
343
|
}
|
|
345
|
-
if (result)
|
|
346
|
-
|
|
347
|
-
|
|
344
|
+
// 修复 fall-through:原代码 `if (result) resolve(...); resolve([])` 第二行
|
|
345
|
+
// 是 no-op,但写法埋雷。
|
|
346
|
+
if (result) {
|
|
347
|
+
return result.map((i) => Object.values(i)[0]);
|
|
348
|
+
}
|
|
349
|
+
return [];
|
|
348
350
|
}
|
|
349
351
|
catch (error) {
|
|
350
352
|
globalThis[_LoggerService].error(`
|
|
@@ -352,9 +354,9 @@ class MysqlConnection {
|
|
|
352
354
|
sql: ${sql},
|
|
353
355
|
params: ${params}
|
|
354
356
|
`);
|
|
355
|
-
|
|
357
|
+
throw error;
|
|
356
358
|
}
|
|
357
|
-
});
|
|
359
|
+
})();
|
|
358
360
|
}
|
|
359
361
|
query(sync, sql, params) {
|
|
360
362
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -370,13 +372,13 @@ class MysqlConnection {
|
|
|
370
372
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
371
373
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
372
374
|
}
|
|
373
|
-
return
|
|
375
|
+
return (async () => {
|
|
374
376
|
try {
|
|
375
377
|
const [result] = await this[_daoConnection].query(sql, params);
|
|
376
378
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
377
379
|
globalThis[_LoggerService].verbose?.(result);
|
|
378
380
|
}
|
|
379
|
-
|
|
381
|
+
return result;
|
|
380
382
|
}
|
|
381
383
|
catch (error) {
|
|
382
384
|
globalThis[_LoggerService].error(`
|
|
@@ -384,9 +386,9 @@ class MysqlConnection {
|
|
|
384
386
|
sql: ${sql},
|
|
385
387
|
params: ${params}
|
|
386
388
|
`);
|
|
387
|
-
|
|
389
|
+
throw error;
|
|
388
390
|
}
|
|
389
|
-
});
|
|
391
|
+
})();
|
|
390
392
|
}
|
|
391
393
|
release(sync) {
|
|
392
394
|
try {
|
|
@@ -435,17 +437,17 @@ export class Mysql {
|
|
|
435
437
|
return null;
|
|
436
438
|
}
|
|
437
439
|
;
|
|
438
|
-
return
|
|
440
|
+
return (async () => {
|
|
439
441
|
try {
|
|
440
442
|
const connection = await this[_daoDB].getConnection();
|
|
441
443
|
globalThis[_LoggerService].debug?.('create new connection!');
|
|
442
|
-
|
|
444
|
+
return new MysqlConnection(connection);
|
|
443
445
|
}
|
|
444
446
|
catch (error) {
|
|
445
447
|
globalThis[_LoggerService].error(error.message, { cause: error });
|
|
446
|
-
|
|
448
|
+
throw error;
|
|
447
449
|
}
|
|
448
|
-
});
|
|
450
|
+
})();
|
|
449
451
|
}
|
|
450
452
|
transaction(sync, fn, conn) {
|
|
451
453
|
if (sync === SyncMode.Sync) {
|
|
@@ -453,7 +455,7 @@ export class Mysql {
|
|
|
453
455
|
return null;
|
|
454
456
|
}
|
|
455
457
|
;
|
|
456
|
-
return
|
|
458
|
+
return (async () => {
|
|
457
459
|
let needCommit = false;
|
|
458
460
|
let newConn = false;
|
|
459
461
|
if (!conn) {
|
|
@@ -474,7 +476,7 @@ export class Mysql {
|
|
|
474
476
|
await conn[_daoConnection].commit();
|
|
475
477
|
globalThis[_LoggerService].debug?.('commit end!');
|
|
476
478
|
}
|
|
477
|
-
|
|
479
|
+
return result;
|
|
478
480
|
}
|
|
479
481
|
catch (error) {
|
|
480
482
|
if (needCommit) {
|
|
@@ -483,7 +485,7 @@ export class Mysql {
|
|
|
483
485
|
globalThis[_LoggerService].debug?.('rollback end!');
|
|
484
486
|
}
|
|
485
487
|
globalThis[_LoggerService].error(error.message, { cause: error });
|
|
486
|
-
|
|
488
|
+
throw error;
|
|
487
489
|
}
|
|
488
490
|
finally {
|
|
489
491
|
try {
|
|
@@ -501,7 +503,7 @@ export class Mysql {
|
|
|
501
503
|
globalThis[_LoggerService].warn?.('Failed to release connection in finally block', error);
|
|
502
504
|
}
|
|
503
505
|
}
|
|
504
|
-
});
|
|
506
|
+
})();
|
|
505
507
|
}
|
|
506
508
|
close(sync) {
|
|
507
509
|
this.isClosing = true;
|
|
@@ -536,7 +538,7 @@ class PostgresqlConnection {
|
|
|
536
538
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
537
539
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
538
540
|
}
|
|
539
|
-
return
|
|
541
|
+
return (async () => {
|
|
540
542
|
try {
|
|
541
543
|
const { rowCount } = await this[_daoConnection].query({
|
|
542
544
|
text: replacePlaceholders(sql),
|
|
@@ -546,7 +548,7 @@ class PostgresqlConnection {
|
|
|
546
548
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
547
549
|
globalThis[_LoggerService].verbose?.(result);
|
|
548
550
|
}
|
|
549
|
-
|
|
551
|
+
return { affectedRows: rowCount || 0, insertId: 0n };
|
|
550
552
|
}
|
|
551
553
|
catch (error) {
|
|
552
554
|
globalThis[_LoggerService].error(`
|
|
@@ -554,9 +556,9 @@ class PostgresqlConnection {
|
|
|
554
556
|
sql: ${sql},
|
|
555
557
|
params: ${params}
|
|
556
558
|
`);
|
|
557
|
-
|
|
559
|
+
throw error;
|
|
558
560
|
}
|
|
559
|
-
});
|
|
561
|
+
})();
|
|
560
562
|
}
|
|
561
563
|
pluck(sync, sql, params) {
|
|
562
564
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -572,20 +574,19 @@ class PostgresqlConnection {
|
|
|
572
574
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
573
575
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
574
576
|
}
|
|
575
|
-
return
|
|
577
|
+
return (async () => {
|
|
576
578
|
try {
|
|
577
579
|
const { rows } = await this[_daoConnection].query({
|
|
578
580
|
text: replacePlaceholders(sql),
|
|
579
581
|
values: params
|
|
580
582
|
});
|
|
583
|
+
// 修复 fall-through:原代码 if 分支 resolve 后没 return,导致 resolve(null)
|
|
584
|
+
// 紧跟其后(虽然第二次 resolve 是 no-op,但写法有歧义)。
|
|
581
585
|
if (rows && rows[0]) {
|
|
582
586
|
const r = Object.values(rows[0])[0];
|
|
583
|
-
|
|
584
|
-
resolve(r);
|
|
585
|
-
else
|
|
586
|
-
resolve(r);
|
|
587
|
+
return r === null ? null : r;
|
|
587
588
|
}
|
|
588
|
-
|
|
589
|
+
return null;
|
|
589
590
|
}
|
|
590
591
|
catch (error) {
|
|
591
592
|
globalThis[_LoggerService].error(`
|
|
@@ -593,9 +594,9 @@ class PostgresqlConnection {
|
|
|
593
594
|
sql: ${sql},
|
|
594
595
|
params: ${params}
|
|
595
596
|
`);
|
|
596
|
-
|
|
597
|
+
throw error;
|
|
597
598
|
}
|
|
598
|
-
});
|
|
599
|
+
})();
|
|
599
600
|
}
|
|
600
601
|
get(sync, sql, params) {
|
|
601
602
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -611,7 +612,7 @@ class PostgresqlConnection {
|
|
|
611
612
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
612
613
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
613
614
|
}
|
|
614
|
-
return
|
|
615
|
+
return (async () => {
|
|
615
616
|
try {
|
|
616
617
|
const { rows } = await this[_daoConnection].query({
|
|
617
618
|
text: replacePlaceholders(sql),
|
|
@@ -620,9 +621,10 @@ class PostgresqlConnection {
|
|
|
620
621
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
621
622
|
globalThis[_LoggerService].verbose?.(rows);
|
|
622
623
|
}
|
|
623
|
-
if (rows && rows[0])
|
|
624
|
-
|
|
625
|
-
|
|
624
|
+
if (rows && rows[0]) {
|
|
625
|
+
return rows[0];
|
|
626
|
+
}
|
|
627
|
+
return null;
|
|
626
628
|
}
|
|
627
629
|
catch (error) {
|
|
628
630
|
globalThis[_LoggerService].error(`
|
|
@@ -630,9 +632,9 @@ class PostgresqlConnection {
|
|
|
630
632
|
sql: ${sql},
|
|
631
633
|
params: ${params}
|
|
632
634
|
`);
|
|
633
|
-
|
|
635
|
+
throw error;
|
|
634
636
|
}
|
|
635
|
-
});
|
|
637
|
+
})();
|
|
636
638
|
}
|
|
637
639
|
raw(sync, sql, params) {
|
|
638
640
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -648,7 +650,7 @@ class PostgresqlConnection {
|
|
|
648
650
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
649
651
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
650
652
|
}
|
|
651
|
-
return
|
|
653
|
+
return (async () => {
|
|
652
654
|
try {
|
|
653
655
|
const { rows } = await this[_daoConnection].query({
|
|
654
656
|
text: replacePlaceholders(sql),
|
|
@@ -657,9 +659,10 @@ class PostgresqlConnection {
|
|
|
657
659
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
658
660
|
globalThis[_LoggerService].verbose?.(rows);
|
|
659
661
|
}
|
|
660
|
-
if (rows)
|
|
661
|
-
|
|
662
|
-
|
|
662
|
+
if (rows) {
|
|
663
|
+
return rows.map((i) => Object.values(i)[0]);
|
|
664
|
+
}
|
|
665
|
+
return [];
|
|
663
666
|
}
|
|
664
667
|
catch (error) {
|
|
665
668
|
globalThis[_LoggerService].error(`
|
|
@@ -667,9 +670,9 @@ class PostgresqlConnection {
|
|
|
667
670
|
sql: ${sql},
|
|
668
671
|
params: ${params}
|
|
669
672
|
`);
|
|
670
|
-
|
|
673
|
+
throw error;
|
|
671
674
|
}
|
|
672
|
-
});
|
|
675
|
+
})();
|
|
673
676
|
}
|
|
674
677
|
query(sync, sql, params) {
|
|
675
678
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -685,7 +688,7 @@ class PostgresqlConnection {
|
|
|
685
688
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
686
689
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
687
690
|
}
|
|
688
|
-
return
|
|
691
|
+
return (async () => {
|
|
689
692
|
try {
|
|
690
693
|
const { rows } = await this[_daoConnection].query({
|
|
691
694
|
text: replacePlaceholders(sql),
|
|
@@ -694,7 +697,7 @@ class PostgresqlConnection {
|
|
|
694
697
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
695
698
|
globalThis[_LoggerService].verbose?.(rows);
|
|
696
699
|
}
|
|
697
|
-
|
|
700
|
+
return rows;
|
|
698
701
|
}
|
|
699
702
|
catch (error) {
|
|
700
703
|
globalThis[_LoggerService].error(`
|
|
@@ -702,9 +705,9 @@ class PostgresqlConnection {
|
|
|
702
705
|
sql: ${sql},
|
|
703
706
|
params: ${params}
|
|
704
707
|
`);
|
|
705
|
-
|
|
708
|
+
throw error;
|
|
706
709
|
}
|
|
707
|
-
});
|
|
710
|
+
})();
|
|
708
711
|
}
|
|
709
712
|
release(sync) {
|
|
710
713
|
try {
|
|
@@ -753,16 +756,16 @@ export class Postgresql {
|
|
|
753
756
|
return null;
|
|
754
757
|
}
|
|
755
758
|
;
|
|
756
|
-
return
|
|
759
|
+
return (async () => {
|
|
757
760
|
try {
|
|
758
761
|
const connection = await this[_daoDB].connect();
|
|
759
762
|
globalThis[_LoggerService].debug?.('create new connection!');
|
|
760
|
-
|
|
763
|
+
return new PostgresqlConnection(connection);
|
|
761
764
|
}
|
|
762
765
|
catch (error) {
|
|
763
|
-
|
|
766
|
+
throw error;
|
|
764
767
|
}
|
|
765
|
-
});
|
|
768
|
+
})();
|
|
766
769
|
}
|
|
767
770
|
transaction(sync, fn, conn) {
|
|
768
771
|
if (sync === SyncMode.Sync) {
|
|
@@ -770,7 +773,7 @@ export class Postgresql {
|
|
|
770
773
|
return null;
|
|
771
774
|
}
|
|
772
775
|
;
|
|
773
|
-
return
|
|
776
|
+
return (async () => {
|
|
774
777
|
let needCommit = false;
|
|
775
778
|
let newConn = false;
|
|
776
779
|
if (!conn) {
|
|
@@ -791,7 +794,7 @@ export class Postgresql {
|
|
|
791
794
|
await conn[_daoConnection].query('COMMIT');
|
|
792
795
|
globalThis[_LoggerService].debug?.('commit end!');
|
|
793
796
|
}
|
|
794
|
-
|
|
797
|
+
return result;
|
|
795
798
|
}
|
|
796
799
|
catch (error) {
|
|
797
800
|
if (needCommit) {
|
|
@@ -800,7 +803,7 @@ export class Postgresql {
|
|
|
800
803
|
globalThis[_LoggerService].debug?.('rollback end!');
|
|
801
804
|
}
|
|
802
805
|
globalThis[_LoggerService].error(error.message, { cause: error });
|
|
803
|
-
|
|
806
|
+
throw error;
|
|
804
807
|
}
|
|
805
808
|
finally {
|
|
806
809
|
try {
|
|
@@ -818,7 +821,7 @@ export class Postgresql {
|
|
|
818
821
|
globalThis[_LoggerService].warn?.('Failed to release connection in finally block', error);
|
|
819
822
|
}
|
|
820
823
|
}
|
|
821
|
-
});
|
|
824
|
+
})();
|
|
822
825
|
}
|
|
823
826
|
close(sync) {
|
|
824
827
|
this.isClosing = true;
|
|
@@ -1087,11 +1090,11 @@ export class SqliteRemoteConnection {
|
|
|
1087
1090
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
1088
1091
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
1089
1092
|
}
|
|
1090
|
-
return
|
|
1093
|
+
return (async () => {
|
|
1091
1094
|
try {
|
|
1092
1095
|
const data = await this[_daoConnection].execute(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
|
|
1093
1096
|
const { affectedRows, insertId } = decode(data, { extensionCodec });
|
|
1094
|
-
|
|
1097
|
+
return { affectedRows, insertId: insertId ? BigInt(insertId) : 0n };
|
|
1095
1098
|
}
|
|
1096
1099
|
catch (error) {
|
|
1097
1100
|
globalThis[_LoggerService].error(`
|
|
@@ -1099,9 +1102,9 @@ export class SqliteRemoteConnection {
|
|
|
1099
1102
|
sql: ${sql},
|
|
1100
1103
|
params: ${params}
|
|
1101
1104
|
`);
|
|
1102
|
-
|
|
1105
|
+
throw error;
|
|
1103
1106
|
}
|
|
1104
|
-
});
|
|
1107
|
+
})();
|
|
1105
1108
|
}
|
|
1106
1109
|
pluck(sync, sql, params) {
|
|
1107
1110
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -1117,11 +1120,10 @@ export class SqliteRemoteConnection {
|
|
|
1117
1120
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
1118
1121
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
1119
1122
|
}
|
|
1120
|
-
return
|
|
1123
|
+
return (async () => {
|
|
1121
1124
|
try {
|
|
1122
1125
|
const data = await this[_daoConnection].pluck(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
|
|
1123
|
-
|
|
1124
|
-
resolve(r);
|
|
1126
|
+
return decode(data, { extensionCodec });
|
|
1125
1127
|
}
|
|
1126
1128
|
catch (error) {
|
|
1127
1129
|
globalThis[_LoggerService].error(`
|
|
@@ -1129,9 +1131,9 @@ export class SqliteRemoteConnection {
|
|
|
1129
1131
|
sql: ${sql},
|
|
1130
1132
|
params: ${params}
|
|
1131
1133
|
`);
|
|
1132
|
-
|
|
1134
|
+
throw error;
|
|
1133
1135
|
}
|
|
1134
|
-
});
|
|
1136
|
+
})();
|
|
1135
1137
|
}
|
|
1136
1138
|
get(sync, sql, params) {
|
|
1137
1139
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -1147,11 +1149,10 @@ export class SqliteRemoteConnection {
|
|
|
1147
1149
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
1148
1150
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
1149
1151
|
}
|
|
1150
|
-
return
|
|
1152
|
+
return (async () => {
|
|
1151
1153
|
try {
|
|
1152
1154
|
const data = await this[_daoConnection].get(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
|
|
1153
|
-
|
|
1154
|
-
resolve(r);
|
|
1155
|
+
return decode(data, { extensionCodec });
|
|
1155
1156
|
}
|
|
1156
1157
|
catch (error) {
|
|
1157
1158
|
globalThis[_LoggerService].error(`
|
|
@@ -1159,9 +1160,9 @@ export class SqliteRemoteConnection {
|
|
|
1159
1160
|
sql: ${sql},
|
|
1160
1161
|
params: ${params}
|
|
1161
1162
|
`);
|
|
1162
|
-
|
|
1163
|
+
throw error;
|
|
1163
1164
|
}
|
|
1164
|
-
});
|
|
1165
|
+
})();
|
|
1165
1166
|
}
|
|
1166
1167
|
raw(sync, sql, params) {
|
|
1167
1168
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -1177,11 +1178,10 @@ export class SqliteRemoteConnection {
|
|
|
1177
1178
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
1178
1179
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
1179
1180
|
}
|
|
1180
|
-
return
|
|
1181
|
+
return (async () => {
|
|
1181
1182
|
try {
|
|
1182
1183
|
const data = await this[_daoConnection].raw(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
|
|
1183
|
-
|
|
1184
|
-
resolve(r);
|
|
1184
|
+
return decode(data, { extensionCodec });
|
|
1185
1185
|
}
|
|
1186
1186
|
catch (error) {
|
|
1187
1187
|
globalThis[_LoggerService].error(`
|
|
@@ -1189,9 +1189,9 @@ export class SqliteRemoteConnection {
|
|
|
1189
1189
|
sql: ${sql},
|
|
1190
1190
|
params: ${params}
|
|
1191
1191
|
`);
|
|
1192
|
-
|
|
1192
|
+
throw error;
|
|
1193
1193
|
}
|
|
1194
|
-
});
|
|
1194
|
+
})();
|
|
1195
1195
|
}
|
|
1196
1196
|
query(sync, sql, params) {
|
|
1197
1197
|
globalThis[_LoggerService].debug?.(sql, params ?? '');
|
|
@@ -1207,11 +1207,10 @@ export class SqliteRemoteConnection {
|
|
|
1207
1207
|
if (globalThis[_GlobalSqlOption].log === 'trace') {
|
|
1208
1208
|
globalThis[_LoggerService].verbose?.(`${sql}\n,${JSON.stringify(params ?? '')}`);
|
|
1209
1209
|
}
|
|
1210
|
-
return
|
|
1210
|
+
return (async () => {
|
|
1211
1211
|
try {
|
|
1212
1212
|
const data = await this[_daoConnection].query(encode([this[_sqliteRemoteName], sql, params], { extensionCodec }));
|
|
1213
|
-
|
|
1214
|
-
resolve(r);
|
|
1213
|
+
return decode(data, { extensionCodec });
|
|
1215
1214
|
}
|
|
1216
1215
|
catch (error) {
|
|
1217
1216
|
globalThis[_LoggerService].error(`
|
|
@@ -1219,9 +1218,9 @@ export class SqliteRemoteConnection {
|
|
|
1219
1218
|
sql: ${sql},
|
|
1220
1219
|
params: ${params}
|
|
1221
1220
|
`);
|
|
1222
|
-
|
|
1221
|
+
throw error;
|
|
1223
1222
|
}
|
|
1224
|
-
});
|
|
1223
|
+
})();
|
|
1225
1224
|
}
|
|
1226
1225
|
release(sync) {
|
|
1227
1226
|
}
|
|
@@ -1238,17 +1237,10 @@ export class SqliteRemote {
|
|
|
1238
1237
|
return null;
|
|
1239
1238
|
}
|
|
1240
1239
|
;
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
try {
|
|
1246
|
-
resolve(this.connection);
|
|
1247
|
-
}
|
|
1248
|
-
catch (error) {
|
|
1249
|
-
reject(error);
|
|
1250
|
-
}
|
|
1251
|
-
});
|
|
1240
|
+
if (!this.connection) {
|
|
1241
|
+
this.connection = new SqliteRemoteConnection(this[_daoDB], this[_sqliteRemoteName]);
|
|
1242
|
+
}
|
|
1243
|
+
return Promise.resolve(this.connection);
|
|
1252
1244
|
}
|
|
1253
1245
|
transaction(sync, fn, conn) {
|
|
1254
1246
|
globalThis[_LoggerService].warn(`SQLITEREMOTE not supported transaction`);
|
|
@@ -1847,6 +1839,19 @@ function P(skipConn = false) {
|
|
|
1847
1839
|
option.dbName = option?.dbName ?? this[_daoDBName] ?? _primaryDB;
|
|
1848
1840
|
option.dbType = this[_dbType] ?? globalThis[_GlobalSqlOption].dbType ?? DBType.Mysql;
|
|
1849
1841
|
option.dao = globalThis[_dao][option.dbType][option.dbName];
|
|
1842
|
+
// 错误日志输出函数:原实现里 `let args = ''` 把外层 rest 参数 `args` 给遮蔽了,
|
|
1843
|
+
// 导致 args.length 永远是 0,params 日志永远打不出来,生产排查时丢失关键上下文。
|
|
1844
|
+
const dumpArgs = (callArgs) => {
|
|
1845
|
+
if (callArgs.length > 0 && callArgs[0] && callArgs[0].params) {
|
|
1846
|
+
try {
|
|
1847
|
+
return JSON.stringify(callArgs[0].params);
|
|
1848
|
+
}
|
|
1849
|
+
catch {
|
|
1850
|
+
return '<unserializable params>';
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
return '';
|
|
1854
|
+
};
|
|
1850
1855
|
if (option.dbType === DBType.Sqlite) {
|
|
1851
1856
|
if (!option.dao) {
|
|
1852
1857
|
const db = new Sqlite(new globalThis[_GlobalSqlOption].BetterSqlite3(option.dbName, { fileMustExist: false }));
|
|
@@ -1870,11 +1875,7 @@ function P(skipConn = false) {
|
|
|
1870
1875
|
return result;
|
|
1871
1876
|
}
|
|
1872
1877
|
catch (error) {
|
|
1873
|
-
|
|
1874
|
-
if (args.length > 0 && args[0].params) {
|
|
1875
|
-
args = JSON.stringify(args[0].params);
|
|
1876
|
-
}
|
|
1877
|
-
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
|
|
1878
|
+
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
|
|
1878
1879
|
throw error;
|
|
1879
1880
|
}
|
|
1880
1881
|
finally {
|
|
@@ -1898,26 +1899,22 @@ function P(skipConn = false) {
|
|
|
1898
1899
|
option.dao = db;
|
|
1899
1900
|
}
|
|
1900
1901
|
Throw.if(option.sync === SyncMode.Sync, 'SqliteRemote remote can not sync!');
|
|
1901
|
-
return
|
|
1902
|
-
// 连接共享
|
|
1903
|
-
if (skipConn === false && !option.conn) {
|
|
1904
|
-
(option).conn = await option.dao.createConnection(SyncMode.Async);
|
|
1905
|
-
}
|
|
1906
|
-
else {
|
|
1907
|
-
needRealseConn = false;
|
|
1908
|
-
}
|
|
1902
|
+
return (async () => {
|
|
1909
1903
|
try {
|
|
1904
|
+
// 连接共享
|
|
1905
|
+
if (skipConn === false && !option.conn) {
|
|
1906
|
+
(option).conn = await option.dao.createConnection(SyncMode.Async);
|
|
1907
|
+
}
|
|
1908
|
+
else {
|
|
1909
|
+
needRealseConn = false;
|
|
1910
|
+
}
|
|
1910
1911
|
const result = await fn.call(this, ...args);
|
|
1911
1912
|
globalThis[_LoggerService].log(`${propertyKey}:${option.sqlId ?? option.tableName}:use ${+new Date() - startTime}ms`);
|
|
1912
|
-
|
|
1913
|
+
return result;
|
|
1913
1914
|
}
|
|
1914
1915
|
catch (error) {
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
args = JSON.stringify(args[0].params);
|
|
1918
|
-
}
|
|
1919
|
-
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
|
|
1920
|
-
reject(error);
|
|
1916
|
+
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
|
|
1917
|
+
throw error;
|
|
1921
1918
|
}
|
|
1922
1919
|
finally {
|
|
1923
1920
|
if (needRealseConn && option && option.conn) {
|
|
@@ -1928,11 +1925,11 @@ function P(skipConn = false) {
|
|
|
1928
1925
|
}
|
|
1929
1926
|
}
|
|
1930
1927
|
}
|
|
1931
|
-
});
|
|
1928
|
+
})();
|
|
1932
1929
|
}
|
|
1933
1930
|
else if (option.dbType === DBType.Mysql) {
|
|
1934
1931
|
Throw.if(!option.dao, `not found db:${String(option.dbName)}(${option.dbType})`);
|
|
1935
|
-
return
|
|
1932
|
+
return (async () => {
|
|
1936
1933
|
try {
|
|
1937
1934
|
// 连接共享
|
|
1938
1935
|
if (skipConn === false && !option.conn) {
|
|
@@ -1943,15 +1940,11 @@ function P(skipConn = false) {
|
|
|
1943
1940
|
}
|
|
1944
1941
|
const result = await fn.call(this, ...args);
|
|
1945
1942
|
globalThis[_LoggerService].log(`${propertyKey}:${option.sqlId ?? option.tableName}:use ${+new Date() - startTime}ms`);
|
|
1946
|
-
|
|
1943
|
+
return result;
|
|
1947
1944
|
}
|
|
1948
1945
|
catch (error) {
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
args = JSON.stringify(args[0].params);
|
|
1952
|
-
}
|
|
1953
|
-
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
|
|
1954
|
-
reject(error);
|
|
1946
|
+
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
|
|
1947
|
+
throw error;
|
|
1955
1948
|
}
|
|
1956
1949
|
finally {
|
|
1957
1950
|
if (needRealseConn && option && option.conn) {
|
|
@@ -1962,11 +1955,11 @@ function P(skipConn = false) {
|
|
|
1962
1955
|
}
|
|
1963
1956
|
}
|
|
1964
1957
|
}
|
|
1965
|
-
});
|
|
1958
|
+
})();
|
|
1966
1959
|
}
|
|
1967
1960
|
else if (option.dbType === DBType.Postgresql) {
|
|
1968
1961
|
Throw.if(!option.dao, `not found db:${String(option.dbName)}(${option.dbType})`);
|
|
1969
|
-
return
|
|
1962
|
+
return (async () => {
|
|
1970
1963
|
try {
|
|
1971
1964
|
// 连接共享
|
|
1972
1965
|
if (skipConn === false && !option.conn) {
|
|
@@ -1977,15 +1970,11 @@ function P(skipConn = false) {
|
|
|
1977
1970
|
}
|
|
1978
1971
|
const result = await fn.call(this, ...args);
|
|
1979
1972
|
globalThis[_LoggerService].log(`${propertyKey}:${option.sqlId ?? option.tableName}:use ${+new Date() - startTime}ms`);
|
|
1980
|
-
|
|
1973
|
+
return result;
|
|
1981
1974
|
}
|
|
1982
1975
|
catch (error) {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
args = JSON.stringify(args[0].params);
|
|
1986
|
-
}
|
|
1987
|
-
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${args}`);
|
|
1988
|
-
reject(error);
|
|
1976
|
+
console.error(`${option.sqlId ?? option.tableName} service ${propertyKey} have an error:${error}, it's argumens: ${dumpArgs(args)}`);
|
|
1977
|
+
throw error;
|
|
1989
1978
|
}
|
|
1990
1979
|
finally {
|
|
1991
1980
|
if (needRealseConn && option && option.conn) {
|
|
@@ -1996,7 +1985,7 @@ function P(skipConn = false) {
|
|
|
1996
1985
|
}
|
|
1997
1986
|
}
|
|
1998
1987
|
}
|
|
1999
|
-
});
|
|
1988
|
+
})();
|
|
2000
1989
|
}
|
|
2001
1990
|
};
|
|
2002
1991
|
};
|
|
@@ -2368,7 +2357,9 @@ export class SqlService {
|
|
|
2368
2357
|
break;
|
|
2369
2358
|
}
|
|
2370
2359
|
case InsertMode.InsertWithTempTable: {
|
|
2371
|
-
|
|
2360
|
+
// 用 snowflake 替代 Math.random()——Math.random() 在高并发下可能碰撞,
|
|
2361
|
+
// 同一连接里多次 InsertWithTempTable 会因为撞名 DDL 报错。
|
|
2362
|
+
const tableTemp = `${option?.tableName}_${snowflake.generate()}`;
|
|
2372
2363
|
const tableTempESC = tableTemp;
|
|
2373
2364
|
sqls.push({ sql: `DROP TABLE IF EXISTS ${tableTempESC};` });
|
|
2374
2365
|
const finalColumns = new Set();
|
|
@@ -2608,7 +2599,7 @@ export class SqlService {
|
|
|
2608
2599
|
Throw.if(!!option.id && !!this[_ids] && this[_ids].length > 1, 'muit id must set where!');
|
|
2609
2600
|
Throw.if(!!option.id && !!option.where && !option.whereSql, 'id and where only one can set!');
|
|
2610
2601
|
option.mode ?? (option.mode = DeleteMode.Common);
|
|
2611
|
-
const tableTemp = `${option?.tableName}_${
|
|
2602
|
+
const tableTemp = `${option?.tableName}_${snowflake.generate()}`;
|
|
2612
2603
|
const tableTempESC = tableTemp;
|
|
2613
2604
|
const tableNameESC = option?.tableName;
|
|
2614
2605
|
if (option.id) {
|
|
@@ -2754,7 +2745,7 @@ export class SqlService {
|
|
|
2754
2745
|
option.mode ?? (option.mode = SelectMode.Common);
|
|
2755
2746
|
option.templateResult ?? (option.templateResult = TemplateResult.AssertOne);
|
|
2756
2747
|
option.error ?? (option.error = 'error data!');
|
|
2757
|
-
const tableTemp = `${option?.tableName}_${
|
|
2748
|
+
const tableTemp = `${option?.tableName}_${snowflake.generate()}`;
|
|
2758
2749
|
const tableTempESC = tableTemp;
|
|
2759
2750
|
const tableNameESC = option?.tableName;
|
|
2760
2751
|
if (option.id) {
|
|
@@ -2800,23 +2791,18 @@ export class SqlService {
|
|
|
2800
2791
|
return this._template(option.templateResult, result, option.error);
|
|
2801
2792
|
}
|
|
2802
2793
|
else {
|
|
2803
|
-
return
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
await option.conn.execute(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
|
|
2812
|
-
}
|
|
2794
|
+
return (async () => {
|
|
2795
|
+
let result;
|
|
2796
|
+
for (let i = 0; i < sqls.length; i++) {
|
|
2797
|
+
if (i === resultIndex) {
|
|
2798
|
+
result = await option.conn.query(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
|
|
2799
|
+
}
|
|
2800
|
+
else {
|
|
2801
|
+
await option.conn.execute(SyncMode.Async, sqls[i]?.sql, sqls[i]?.params);
|
|
2813
2802
|
}
|
|
2814
|
-
resolve(this._template(option.templateResult, result, option.error));
|
|
2815
|
-
}
|
|
2816
|
-
catch (error) {
|
|
2817
|
-
reject(error);
|
|
2818
2803
|
}
|
|
2819
|
-
|
|
2804
|
+
return this._template(option.templateResult, result, option.error);
|
|
2805
|
+
})();
|
|
2820
2806
|
}
|
|
2821
2807
|
}
|
|
2822
2808
|
_select(templateResult, result, def, errorMsg, hump, mapper, mapperIfUndefined, dataConvert) {
|
|
@@ -2931,15 +2917,10 @@ export class SqlService {
|
|
|
2931
2917
|
return this._select(option.selectResult, result, option.defValue, option.errorMsg, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert);
|
|
2932
2918
|
}
|
|
2933
2919
|
else {
|
|
2934
|
-
return
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
}
|
|
2939
|
-
catch (error) {
|
|
2940
|
-
reject(error);
|
|
2941
|
-
}
|
|
2942
|
-
});
|
|
2920
|
+
return (async () => {
|
|
2921
|
+
const result = await option.conn.query(SyncMode.Async, sql, params);
|
|
2922
|
+
return this._select(option.selectResult, result, option.defValue, option.errorMsg, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert);
|
|
2923
|
+
})();
|
|
2943
2924
|
}
|
|
2944
2925
|
}
|
|
2945
2926
|
selectBatch(option) {
|
|
@@ -2956,15 +2937,10 @@ export class SqlService {
|
|
|
2956
2937
|
return result.map(item => this._select(option.selectResult, item, null, undefined, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert));
|
|
2957
2938
|
}
|
|
2958
2939
|
else {
|
|
2959
|
-
return
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
}
|
|
2964
|
-
catch (error) {
|
|
2965
|
-
reject(error);
|
|
2966
|
-
}
|
|
2967
|
-
});
|
|
2940
|
+
return (async () => {
|
|
2941
|
+
const result = await option.conn.query(SyncMode.Async, sql, params);
|
|
2942
|
+
return result.map(item => this._select(option.selectResult, item, null, undefined, option.hump, option.mapper, option.mapperIfUndefined, option.dataConvert));
|
|
2943
|
+
})();
|
|
2968
2944
|
}
|
|
2969
2945
|
}
|
|
2970
2946
|
excute(option) {
|
|
@@ -2977,15 +2953,10 @@ export class SqlService {
|
|
|
2977
2953
|
return result.affectedRows;
|
|
2978
2954
|
}
|
|
2979
2955
|
else {
|
|
2980
|
-
return
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
}
|
|
2985
|
-
catch (error) {
|
|
2986
|
-
reject(error);
|
|
2987
|
-
}
|
|
2988
|
-
});
|
|
2956
|
+
return (async () => {
|
|
2957
|
+
const result = await option.conn.execute(SyncMode.Async, sql, params);
|
|
2958
|
+
return result.affectedRows;
|
|
2959
|
+
})();
|
|
2989
2960
|
}
|
|
2990
2961
|
}
|
|
2991
2962
|
transaction(option) {
|
|
@@ -2993,15 +2964,7 @@ export class SqlService {
|
|
|
2993
2964
|
return option.dao.transaction(SyncMode.Sync, option.fn, option.conn);
|
|
2994
2965
|
}
|
|
2995
2966
|
else {
|
|
2996
|
-
return
|
|
2997
|
-
try {
|
|
2998
|
-
const rt = await option.dao.transaction(SyncMode.Async, option.fn, option.conn);
|
|
2999
|
-
resolve(rt);
|
|
3000
|
-
}
|
|
3001
|
-
catch (error) {
|
|
3002
|
-
reject(error);
|
|
3003
|
-
}
|
|
3004
|
-
});
|
|
2967
|
+
return option.dao.transaction(SyncMode.Async, option.fn, option.conn);
|
|
3005
2968
|
}
|
|
3006
2969
|
}
|
|
3007
2970
|
stream() {
|
|
@@ -3019,7 +2982,9 @@ export class SqlService {
|
|
|
3019
2982
|
if (option.hump || (option.hump === undefined && globalThis[_Hump]) && option.sortName) {
|
|
3020
2983
|
option.sortName = P2C(option.sortName);
|
|
3021
2984
|
}
|
|
3022
|
-
Object.assign
|
|
2985
|
+
// 不要 Object.assign 进调用方的 params——@P 装饰器对 option 只做了浅拷贝,
|
|
2986
|
+
// option.params 仍指向用户传入的对象,原写法会把分页字段写回去污染下一次调用。
|
|
2987
|
+
option.params = Object.assign({}, option.params, {
|
|
3023
2988
|
limitStart: calc(option.pageNumber).sub(1).mul(option.pageSize).over(),
|
|
3024
2989
|
limitEnd: option.pageSize - 0,
|
|
3025
2990
|
sortName: option.sortName ?? undefined,
|
|
@@ -3081,43 +3046,40 @@ export class SqlService {
|
|
|
3081
3046
|
return result;
|
|
3082
3047
|
}
|
|
3083
3048
|
else {
|
|
3084
|
-
return
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
if (sqlSum) {
|
|
3100
|
-
result.sum = await this.select({
|
|
3101
|
-
...option,
|
|
3102
|
-
sql: sqlSum,
|
|
3103
|
-
sync: SyncMode.Async,
|
|
3104
|
-
selectResult: SelectResult.R_CS_Assert
|
|
3105
|
-
});
|
|
3106
|
-
}
|
|
3107
|
-
if (sql) {
|
|
3108
|
-
result.records = await this.select({
|
|
3109
|
-
...option,
|
|
3110
|
-
sql,
|
|
3111
|
-
sync: SyncMode.Async,
|
|
3112
|
-
selectResult: SelectResult.RS_CS
|
|
3113
|
-
});
|
|
3114
|
-
}
|
|
3115
|
-
resolve(result);
|
|
3049
|
+
return (async () => {
|
|
3050
|
+
if (sqlCount) {
|
|
3051
|
+
result.total = await this.select({
|
|
3052
|
+
...option,
|
|
3053
|
+
sql: sqlCount,
|
|
3054
|
+
sync: SyncMode.Async,
|
|
3055
|
+
selectResult: SelectResult.R_C_Assert
|
|
3056
|
+
});
|
|
3057
|
+
// 修正运算符优先级:`option.pageSize ?? 10 - 1` 被解析为 `pageSize ?? 9`,
|
|
3058
|
+
// 而同步分支用的是 `option.pageSize - 1`,行为不一致。
|
|
3059
|
+
result.size = calc(result.total)
|
|
3060
|
+
.add((option.pageSize ?? 10) - 1)
|
|
3061
|
+
.div(option.pageSize)
|
|
3062
|
+
.round(0, 2)
|
|
3063
|
+
.over();
|
|
3116
3064
|
}
|
|
3117
|
-
|
|
3118
|
-
|
|
3065
|
+
if (sqlSum) {
|
|
3066
|
+
result.sum = await this.select({
|
|
3067
|
+
...option,
|
|
3068
|
+
sql: sqlSum,
|
|
3069
|
+
sync: SyncMode.Async,
|
|
3070
|
+
selectResult: SelectResult.R_CS_Assert
|
|
3071
|
+
});
|
|
3119
3072
|
}
|
|
3120
|
-
|
|
3073
|
+
if (sql) {
|
|
3074
|
+
result.records = await this.select({
|
|
3075
|
+
...option,
|
|
3076
|
+
sql,
|
|
3077
|
+
sync: SyncMode.Async,
|
|
3078
|
+
selectResult: SelectResult.RS_CS
|
|
3079
|
+
});
|
|
3080
|
+
}
|
|
3081
|
+
return result;
|
|
3082
|
+
})();
|
|
3121
3083
|
}
|
|
3122
3084
|
}
|
|
3123
3085
|
/**
|
|
@@ -3219,48 +3181,25 @@ export class SqlService {
|
|
|
3219
3181
|
}
|
|
3220
3182
|
}
|
|
3221
3183
|
else if (option.dbType === DBType.SqliteRemote) {
|
|
3222
|
-
return
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
await option.conn.execute(SyncMode.Async, `ALTER TABLE ${tableES} RENAME TO ${rtable};`);
|
|
3242
|
-
await option.conn.execute(SyncMode.Async, `
|
|
3243
|
-
CREATE TABLE IF NOT EXISTS ${tableES}(
|
|
3244
|
-
${Object.values(this[_fields]).map(K => K[DBType.Sqlite]()).join(',')}
|
|
3245
|
-
${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields][i]?.C2()).join(',')})` : ''}
|
|
3246
|
-
);
|
|
3247
|
-
`);
|
|
3248
|
-
if (this[_index] && this[_index].length) {
|
|
3249
|
-
for (const index of this[_index]) {
|
|
3250
|
-
await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${tableES} ("${this[_fields][index]?.C2()}");`);
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
|
-
await option.conn.execute(SyncMode.Async, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
|
|
3254
|
-
await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
|
|
3255
|
-
// 更新完毕,保存版本号
|
|
3256
|
-
await option.conn.execute(SyncMode.Async, 'UPDATE TABLE_VERSION SET ______version = ? WHERE ______tableName = ?', [option.tableName, lastVersion]);
|
|
3257
|
-
}
|
|
3258
|
-
else if (!tableVersion) { // 不需要升级情况:没有旧的版本号
|
|
3259
|
-
await option.conn.execute(SyncMode.Async, 'INSERT INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
|
|
3260
|
-
}
|
|
3261
|
-
}
|
|
3262
|
-
else { // 表不存在
|
|
3263
|
-
// 创建表
|
|
3184
|
+
return (async () => {
|
|
3185
|
+
if (option?.force) {
|
|
3186
|
+
await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${tableES};`);
|
|
3187
|
+
}
|
|
3188
|
+
const lastVersion = this[_sqlite_version] ?? '1';
|
|
3189
|
+
// 检查表
|
|
3190
|
+
const tableCheckResult = await option.conn.pluck(SyncMode.Async, `SELECT COUNT(1) t FROM sqlite_master WHERE TYPE = 'table' AND name = ?`, [option.tableName]);
|
|
3191
|
+
if (tableCheckResult) {
|
|
3192
|
+
// 旧版本
|
|
3193
|
+
const tableVersion = await option.conn.pluck(SyncMode.Async, 'SELECT ______version v from TABLE_VERSION WHERE ______tableName = ?', [option.tableName]);
|
|
3194
|
+
if (tableVersion && tableVersion < lastVersion) { // 发现需要升级的版本
|
|
3195
|
+
// 更新版本
|
|
3196
|
+
const columns = iterate(await option.conn.query(SyncMode.Async, `PRAGMA table_info(${tableES})`))
|
|
3197
|
+
.filter(c => this[_fields].hasOwnProperty(C2P(c.name, globalThis[_Hump])))
|
|
3198
|
+
.map(c => c.name)
|
|
3199
|
+
.join(',');
|
|
3200
|
+
const rtable = `${option.tableName}_${tableVersion.replace(/\./, '_')}`;
|
|
3201
|
+
await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
|
|
3202
|
+
await option.conn.execute(SyncMode.Async, `ALTER TABLE ${tableES} RENAME TO ${rtable};`);
|
|
3264
3203
|
await option.conn.execute(SyncMode.Async, `
|
|
3265
3204
|
CREATE TABLE IF NOT EXISTS ${tableES}(
|
|
3266
3205
|
${Object.values(this[_fields]).map(K => K[DBType.Sqlite]()).join(',')}
|
|
@@ -3269,17 +3208,34 @@ export class SqlService {
|
|
|
3269
3208
|
`);
|
|
3270
3209
|
if (this[_index] && this[_index].length) {
|
|
3271
3210
|
for (const index of this[_index]) {
|
|
3272
|
-
await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${
|
|
3211
|
+
await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${tableES} ("${this[_fields][index]?.C2()}");`);
|
|
3273
3212
|
}
|
|
3274
3213
|
}
|
|
3275
|
-
await option.conn.execute(SyncMode.Async,
|
|
3214
|
+
await option.conn.execute(SyncMode.Async, `INSERT INTO ${tableES} (${columns}) SELECT ${columns} FROM ${rtable};`);
|
|
3215
|
+
await option.conn.execute(SyncMode.Async, `DROP TABLE IF EXISTS ${rtable};`);
|
|
3216
|
+
// 更新完毕,保存版本号
|
|
3217
|
+
await option.conn.execute(SyncMode.Async, 'UPDATE TABLE_VERSION SET ______version = ? WHERE ______tableName = ?', [option.tableName, lastVersion]);
|
|
3218
|
+
}
|
|
3219
|
+
else if (!tableVersion) { // 不需要升级情况:没有旧的版本号
|
|
3220
|
+
await option.conn.execute(SyncMode.Async, 'INSERT INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
|
|
3276
3221
|
}
|
|
3277
|
-
resolve();
|
|
3278
3222
|
}
|
|
3279
|
-
|
|
3280
|
-
|
|
3223
|
+
else { // 表不存在
|
|
3224
|
+
// 创建表
|
|
3225
|
+
await option.conn.execute(SyncMode.Async, `
|
|
3226
|
+
CREATE TABLE IF NOT EXISTS ${tableES}(
|
|
3227
|
+
${Object.values(this[_fields]).map(K => K[DBType.Sqlite]()).join(',')}
|
|
3228
|
+
${this[_ids] && this[_ids].length ? `, PRIMARY KEY (${this[_ids].map(i => this[_fields][i]?.C2()).join(',')})` : ''}
|
|
3229
|
+
);
|
|
3230
|
+
`);
|
|
3231
|
+
if (this[_index] && this[_index].length) {
|
|
3232
|
+
for (const index of this[_index]) {
|
|
3233
|
+
await option.conn.execute(SyncMode.Async, `CREATE INDEX ${`${index}_${Math.random()}`.replace(/\./, '')} ON ${option.tableName} ("${this[_fields][index]?.C2()}");`);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
await option.conn.execute(SyncMode.Async, 'INSERT OR REPLACE INTO TABLE_VERSION (______tableName, ______version ) VALUES ( ?, ? )', [option.tableName, lastVersion]);
|
|
3281
3237
|
}
|
|
3282
|
-
});
|
|
3238
|
+
})();
|
|
3283
3239
|
}
|
|
3284
3240
|
}
|
|
3285
3241
|
close(option) {
|
|
@@ -4022,33 +3978,28 @@ class StreamQuery {
|
|
|
4022
3978
|
return result;
|
|
4023
3979
|
}
|
|
4024
3980
|
else {
|
|
4025
|
-
return
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
}
|
|
4048
|
-
catch (error) {
|
|
4049
|
-
reject(error);
|
|
4050
|
-
}
|
|
4051
|
-
});
|
|
3981
|
+
return (async () => {
|
|
3982
|
+
result.total = await this._service.select({
|
|
3983
|
+
...option,
|
|
3984
|
+
params,
|
|
3985
|
+
sql: sqlCount,
|
|
3986
|
+
sync: SyncMode.Async,
|
|
3987
|
+
selectResult: SelectResult.R_C_Assert
|
|
3988
|
+
});
|
|
3989
|
+
result.size = calc(result.total)
|
|
3990
|
+
.add(this._pageSize - 1)
|
|
3991
|
+
.div(this._pageSize)
|
|
3992
|
+
.round(0, 2)
|
|
3993
|
+
.over();
|
|
3994
|
+
result.records = await this._service.select({
|
|
3995
|
+
...option,
|
|
3996
|
+
params,
|
|
3997
|
+
sql,
|
|
3998
|
+
sync: SyncMode.Async,
|
|
3999
|
+
selectResult: SelectResult.RS_CS
|
|
4000
|
+
});
|
|
4001
|
+
return result;
|
|
4002
|
+
})();
|
|
4052
4003
|
}
|
|
4053
4004
|
}
|
|
4054
4005
|
excuteUpdate(option) {
|
|
@@ -5127,42 +5078,54 @@ export function getRedisDB(db) {
|
|
|
5127
5078
|
return rd;
|
|
5128
5079
|
}
|
|
5129
5080
|
/**
|
|
5130
|
-
redlock
|
|
5081
|
+
redlock —— 用 redlock 做一把元锁([lockex]key),把"读 count、判断 count、incr"做成原子操作。
|
|
5082
|
+
原实现失败时 `return await GetRedisLock(...)` 递归调用自己,redis 长时间不可用会栈溢出;
|
|
5083
|
+
改成循环 + 指数退避 + 最大重试上限,达到上限后抛出(让上层决定是否走降级路径)。
|
|
5131
5084
|
*/
|
|
5085
|
+
const GET_REDIS_LOCK_MAX_RETRIES = 10;
|
|
5086
|
+
const GET_REDIS_LOCK_BASE_DELAY = 50; // 毫秒
|
|
5132
5087
|
export async function GetRedisLock(key, lockMaxActive) {
|
|
5133
5088
|
const lock = globalThis[_dao][DBType.RedisLock];
|
|
5134
5089
|
Throw.if(!lock, 'not found lock!');
|
|
5135
5090
|
const db = getRedisDB();
|
|
5136
|
-
let
|
|
5137
|
-
|
|
5138
|
-
initLock
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
await db.
|
|
5142
|
-
|
|
5091
|
+
let lastError;
|
|
5092
|
+
for (let attempt = 0; attempt < GET_REDIS_LOCK_MAX_RETRIES; attempt++) {
|
|
5093
|
+
let initLock;
|
|
5094
|
+
try {
|
|
5095
|
+
initLock = await lock.acquire([`[lockex]${key}`], 5000);
|
|
5096
|
+
const count = await db.get(key);
|
|
5097
|
+
if (count === null || parseInt(count) < (lockMaxActive ?? 1)) {
|
|
5098
|
+
await db.incr(key);
|
|
5099
|
+
return true;
|
|
5100
|
+
}
|
|
5101
|
+
else {
|
|
5102
|
+
return false;
|
|
5103
|
+
}
|
|
5143
5104
|
}
|
|
5144
|
-
|
|
5145
|
-
|
|
5105
|
+
catch (er) {
|
|
5106
|
+
lastError = er;
|
|
5107
|
+
// 指数退避,避免 redis 抖动时打死服务
|
|
5108
|
+
const delay = Math.min(GET_REDIS_LOCK_BASE_DELAY * Math.pow(2, attempt), 1000);
|
|
5109
|
+
globalThis[_LoggerService].debug?.(`GetRedisLock ${key} attempt ${attempt + 1} failed: ${er?.message}, retry after ${delay}ms`);
|
|
5110
|
+
await sleep(delay);
|
|
5146
5111
|
}
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
// eslint-disable-next-line no-empty
|
|
5156
|
-
}
|
|
5157
|
-
catch (error) {
|
|
5112
|
+
finally {
|
|
5113
|
+
if (initLock) {
|
|
5114
|
+
try {
|
|
5115
|
+
await initLock.release();
|
|
5116
|
+
// eslint-disable-next-line no-empty
|
|
5117
|
+
}
|
|
5118
|
+
catch (error) {
|
|
5119
|
+
}
|
|
5158
5120
|
}
|
|
5159
5121
|
}
|
|
5160
5122
|
}
|
|
5123
|
+
throw new Error(`GetRedisLock ${key} failed after ${GET_REDIS_LOCK_MAX_RETRIES} retries: ${lastError?.message ?? lastError}`);
|
|
5161
5124
|
}
|
|
5162
5125
|
;
|
|
5163
5126
|
/** 对FN加锁、缓存执行 */
|
|
5164
5127
|
export async function excuteWithLock(config, fn__) {
|
|
5165
|
-
const key =
|
|
5128
|
+
const key = `[lock]${typeof config.key === 'function' ? config.key() : config.key}`;
|
|
5166
5129
|
const db = getRedisDB();
|
|
5167
5130
|
let wait_time = 0;
|
|
5168
5131
|
const fn = async () => {
|
|
@@ -5193,43 +5156,72 @@ export async function excuteWithLock(config, fn__) {
|
|
|
5193
5156
|
};
|
|
5194
5157
|
return await fn();
|
|
5195
5158
|
}
|
|
5196
|
-
/**
|
|
5159
|
+
/**
|
|
5160
|
+
* 方法加锁装饰器。
|
|
5161
|
+
* 与 `MethodCache` 组合时,**不要**手工叠装饰器——`MethodCache` 内部已经实现了
|
|
5162
|
+
* "先查缓存 → miss 才加锁 → 锁内二次检查 → 仍 miss 才执行" 的 single-flight 语义,
|
|
5163
|
+
* 直接给方法加 `@MethodCache` 即可。`@MethodLock` 单独使用时仅做并发互斥,不感知缓存。
|
|
5164
|
+
*/
|
|
5197
5165
|
export function MethodLock(config) {
|
|
5198
5166
|
return function (target, _propertyKey, descriptor) {
|
|
5199
5167
|
const fn__ = descriptor.value;
|
|
5200
5168
|
descriptor.value = async function (...args) {
|
|
5201
|
-
|
|
5202
|
-
|
|
5169
|
+
// 注意:装饰器闭包里的 `config` 是所有调用共享的同一个对象,
|
|
5170
|
+
// 不能把 per-call 的状态写回去——必须每次构造一份新的 config 传给 excuteWithLock。
|
|
5171
|
+
const resolvedKey = typeof config.key === 'function' ? config.key.call(this, ...args) : config.key;
|
|
5172
|
+
const perCallConfig = { ...config, key: resolvedKey };
|
|
5173
|
+
return await excuteWithLock(perCallConfig, async () => await fn__.call(this, ...args));
|
|
5203
5174
|
};
|
|
5204
5175
|
};
|
|
5205
5176
|
}
|
|
5206
5177
|
/** 设置方法缓存 */
|
|
5207
5178
|
async function setMethodCache(config, devid) {
|
|
5208
5179
|
const db = getRedisDB();
|
|
5209
|
-
|
|
5210
|
-
|
|
5180
|
+
const isNullish = config.result === null || config.result === undefined;
|
|
5181
|
+
// 旧行为:null / undefined 不进缓存;新增 cacheNullValue=true 时写入 'null'(穿透防御)。
|
|
5182
|
+
if (isNullish && !config.cacheNullValue) {
|
|
5183
|
+
return;
|
|
5184
|
+
}
|
|
5185
|
+
// 统一序列化:undefined 不是合法 JSON,归一化成 null。
|
|
5186
|
+
const payload = isNullish ? 'null' : JSON.stringify(config.result);
|
|
5187
|
+
// 负缓存通常用更短 TTL,避免业务恢复后还长时间返回旧的 null。
|
|
5188
|
+
const ttl = isNullish
|
|
5189
|
+
? (config.nullCacheTime ?? config.autoClearTime)
|
|
5190
|
+
: config.autoClearTime;
|
|
5191
|
+
// 映射关系存放(负缓存也参与关联清除,因为外部清缓存的语义不区分正负)
|
|
5192
|
+
if (config.clearKey && config.clearKey.length > 0) {
|
|
5193
|
+
for (const clear of config.clearKey) {
|
|
5194
|
+
await db.sadd(`[cache-parent]${clear}`, config.key);
|
|
5195
|
+
await db.sadd(`[cache-child]${config.key}`, clear);
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
if (ttl) { // 自动清空
|
|
5199
|
+
await db.set(`[cache]${config.key}`, payload, 'EX', ttl * 60);
|
|
5200
|
+
globalThis[_LoggerService].debug?.(`cache ${config.key} seted${isNullish ? ' (null)' : ''}!`);
|
|
5201
|
+
// 订阅:清空 clear list —— 同一 key 在缓存生命周期内只注册一次监听器,
|
|
5202
|
+
// 否则每次 miss 都会 .on 一次,clearMethodCache 触发时回调会被执行 N 遍,
|
|
5203
|
+
// 长期运行造成监听器泄漏。
|
|
5211
5204
|
if (config.clearKey && config.clearKey.length > 0) {
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
}
|
|
5216
|
-
}
|
|
5217
|
-
if (config.autoClearTime) { // 自动清空
|
|
5218
|
-
await db.set(`[cache]${config.key}`, JSON.stringify(config.result), 'EX', config.autoClearTime * 60);
|
|
5219
|
-
// 订阅:清空 clear list
|
|
5220
|
-
if (config.clearKey && config.clearKey.length > 0) {
|
|
5221
|
-
globalThis[_EventBus].on(`[cache]${config.key}`, async (key) => {
|
|
5205
|
+
const event = `[cache]${config.key}`;
|
|
5206
|
+
if (globalThis[_EventBus].listenerCount(event) === 0) {
|
|
5207
|
+
globalThis[_EventBus].on(event, async (key) => {
|
|
5222
5208
|
await clearChild(key, true);
|
|
5209
|
+
globalThis[_LoggerService].debug?.(`cache ${key} clear by key!`);
|
|
5223
5210
|
});
|
|
5224
5211
|
}
|
|
5225
5212
|
}
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
}
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5213
|
+
}
|
|
5214
|
+
else {
|
|
5215
|
+
await db.set(`[cache]${config.key}`, payload);
|
|
5216
|
+
globalThis[_LoggerService].debug?.(`cache ${config.key} seted${isNullish ? ' (null)' : ''}!`);
|
|
5217
|
+
}
|
|
5218
|
+
if (devid) {
|
|
5219
|
+
// 订阅:清空 clear list —— 同样去重,避免每次 miss 都重复注册。
|
|
5220
|
+
const event = `user-${devid}`;
|
|
5221
|
+
if (globalThis[_EventBus].listenerCount(event) === 0) {
|
|
5222
|
+
globalThis[_EventBus].on(event, async function (key) {
|
|
5232
5223
|
await clearChild(key);
|
|
5224
|
+
globalThis[_LoggerService].debug?.(`cache ${key} clear by devid!`);
|
|
5233
5225
|
});
|
|
5234
5226
|
}
|
|
5235
5227
|
}
|
|
@@ -5274,53 +5266,93 @@ async function clearParent(clearKey) {
|
|
|
5274
5266
|
}
|
|
5275
5267
|
}
|
|
5276
5268
|
/**
|
|
5277
|
-
*
|
|
5278
|
-
*
|
|
5269
|
+
* 缓存执行的核心:先查缓存 → miss 才加锁 → 锁内二次检查 → 仍 miss 才执行原方法 → 写缓存。
|
|
5270
|
+
* 这样同一 key 的并发 N 个请求里,只会有 1 个真正穿透到 fn,其余等待者在锁内读到刚写入的缓存。
|
|
5271
|
+
* 锁本身有等待超时和锁 TTL 兜底,redis 不可用时 fallback 到无锁直跑(保留可用性,退化为旧行为)。
|
|
5272
|
+
*
|
|
5273
|
+
* 命中判断用 `cached !== null`(ioredis 在 key 不存在时返回 `null`),
|
|
5274
|
+
* 这样 `cacheNullValue=true` 时存进去的字面量 `'null'` 也会算作命中,达到防穿透效果。
|
|
5279
5275
|
*/
|
|
5280
|
-
|
|
5276
|
+
async function excuteCacheCore(opts) {
|
|
5281
5277
|
const db = getRedisDB();
|
|
5282
|
-
const
|
|
5283
|
-
const
|
|
5284
|
-
if (
|
|
5285
|
-
globalThis[_LoggerService].debug?.(`cache ${key} hit!`);
|
|
5286
|
-
return JSON.parse(
|
|
5287
|
-
}
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5278
|
+
const cacheKey = `[cache]${opts.key}`;
|
|
5279
|
+
const cached = await db.get(cacheKey);
|
|
5280
|
+
if (cached !== null) {
|
|
5281
|
+
globalThis[_LoggerService].debug?.(`cache ${opts.key} hit!`);
|
|
5282
|
+
return JSON.parse(cached);
|
|
5283
|
+
}
|
|
5284
|
+
globalThis[_LoggerService].debug?.(`cache ${opts.key} miss!`);
|
|
5285
|
+
const setOpts = {
|
|
5286
|
+
clearKey: opts.clearKey,
|
|
5287
|
+
autoClearTime: opts.autoClearTime,
|
|
5288
|
+
cacheNullValue: opts.cacheNullValue,
|
|
5289
|
+
nullCacheTime: opts.nullCacheTime
|
|
5290
|
+
};
|
|
5291
|
+
// 没有 redlock 配置时直接退化为旧行为(无锁直跑)。
|
|
5292
|
+
const lockDao = globalThis[_dao][DBType.RedisLock];
|
|
5293
|
+
if (!lockDao) {
|
|
5294
|
+
const result = await opts.fn();
|
|
5295
|
+
await setMethodCache({ key: opts.key, ...setOpts, result }, opts.devid);
|
|
5297
5296
|
return result;
|
|
5298
5297
|
}
|
|
5298
|
+
return await excuteWithLock({
|
|
5299
|
+
key: opts.key,
|
|
5300
|
+
// 让等待者尽快读到首飞写入的缓存
|
|
5301
|
+
lockWait: true,
|
|
5302
|
+
lockRetryInterval: 100,
|
|
5303
|
+
// 锁 TTL 给一个相对合理的上限,避免首飞挂掉时其它请求永远卡住
|
|
5304
|
+
lockMaxTime: 30000
|
|
5305
|
+
}, async () => {
|
|
5306
|
+
// 锁内二次检查:等待者醒来后必须再读一次缓存,否则会和首飞一样跑一遍原方法。
|
|
5307
|
+
const recheck = await db.get(cacheKey);
|
|
5308
|
+
if (recheck !== null) {
|
|
5309
|
+
globalThis[_LoggerService].debug?.(`cache ${opts.key} hit after lock!`);
|
|
5310
|
+
return JSON.parse(recheck);
|
|
5311
|
+
}
|
|
5312
|
+
const result = await opts.fn();
|
|
5313
|
+
await setMethodCache({ key: opts.key, ...setOpts, result }, opts.devid);
|
|
5314
|
+
return result;
|
|
5315
|
+
});
|
|
5316
|
+
}
|
|
5317
|
+
/**
|
|
5318
|
+
* 执行一个方法fn,
|
|
5319
|
+
* 如果有缓存,则返回缓存,否则【加锁、二次检查】后执行方法并缓存,防止缓存击穿。
|
|
5320
|
+
* 可选 `cacheNullValue=true` 开启负缓存,防穿透;负缓存默认沿用 autoClearTime,可单独用 nullCacheTime 设短。
|
|
5321
|
+
*/
|
|
5322
|
+
export async function excuteWithCache(config, fn) {
|
|
5323
|
+
const key = typeof config.key === 'function' ? config.key() : config.key;
|
|
5324
|
+
return await excuteCacheCore({
|
|
5325
|
+
key,
|
|
5326
|
+
clearKey: config.clearKey,
|
|
5327
|
+
autoClearTime: config.autoClearTime,
|
|
5328
|
+
cacheNullValue: config.cacheNullValue,
|
|
5329
|
+
nullCacheTime: config.nullCacheTime,
|
|
5330
|
+
fn
|
|
5331
|
+
});
|
|
5299
5332
|
}
|
|
5300
|
-
/**
|
|
5333
|
+
/**
|
|
5334
|
+
* 缓存注解:先查缓存 → miss 才加锁 → 锁内再查一次 → 仍 miss 才执行原方法。
|
|
5335
|
+
* 已内置 single-flight,不需要再叠 `@MethodLock`。
|
|
5336
|
+
* 可选 `cacheNullValue=true` 开启负缓存,防穿透。
|
|
5337
|
+
*/
|
|
5301
5338
|
export function MethodCache(config) {
|
|
5302
|
-
return function (
|
|
5339
|
+
return function (_target, _propertyKey, descriptor) {
|
|
5303
5340
|
const fn = descriptor.value;
|
|
5304
5341
|
descriptor.value = async function (...args) {
|
|
5305
5342
|
const key = typeof config.key === 'function' ? config.key.call(this, ...args) : config.key;
|
|
5306
|
-
const
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
autoClearTime: config.autoClearTime,
|
|
5320
|
-
result
|
|
5321
|
-
}, config.clearWithSession && this.ctx.me && this.ctx.me.devid);
|
|
5322
|
-
return result;
|
|
5323
|
-
}
|
|
5343
|
+
const clearKey = config.clearKey
|
|
5344
|
+
? (typeof config.clearKey === 'function' ? config.clearKey.call(this, ...args) : config.clearKey)
|
|
5345
|
+
: undefined;
|
|
5346
|
+
const devid = config.clearWithSession && this.ctx && this.ctx.me && this.ctx.me.devid;
|
|
5347
|
+
return await excuteCacheCore({
|
|
5348
|
+
key,
|
|
5349
|
+
clearKey,
|
|
5350
|
+
autoClearTime: config.autoClearTime,
|
|
5351
|
+
cacheNullValue: config.cacheNullValue,
|
|
5352
|
+
nullCacheTime: config.nullCacheTime,
|
|
5353
|
+
devid,
|
|
5354
|
+
fn: async () => await fn.call(this, ...args)
|
|
5355
|
+
});
|
|
5324
5356
|
};
|
|
5325
5357
|
};
|
|
5326
5358
|
}
|