monsqlize 1.0.0 → 1.0.2
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/CHANGELOG.md +92 -2419
- package/README.md +630 -1070
- package/index.d.ts +252 -15
- package/lib/cache.js +8 -8
- package/lib/common/validation.js +64 -1
- package/lib/connect.js +3 -3
- package/lib/errors.js +10 -0
- package/lib/index.js +118 -9
- package/lib/infrastructure/ssh-tunnel-ssh2.js +211 -0
- package/lib/infrastructure/ssh-tunnel.js +40 -0
- package/lib/infrastructure/uri-parser.js +35 -0
- package/lib/lock/Lock.js +66 -0
- package/lib/lock/errors.js +27 -0
- package/lib/lock/index.js +12 -0
- package/lib/logger.js +1 -1
- package/lib/model/examples/test.js +4 -4
- package/lib/mongodb/common/accessor-helpers.js +17 -3
- package/lib/mongodb/connect.js +68 -13
- package/lib/mongodb/index.js +140 -7
- package/lib/mongodb/management/collection-ops.js +4 -4
- package/lib/mongodb/management/index-ops.js +18 -18
- package/lib/mongodb/management/validation-ops.js +3 -3
- package/lib/mongodb/queries/aggregate.js +14 -5
- package/lib/mongodb/queries/chain.js +52 -45
- package/lib/mongodb/queries/count.js +16 -6
- package/lib/mongodb/queries/distinct.js +15 -6
- package/lib/mongodb/queries/find-and-count.js +22 -13
- package/lib/mongodb/queries/find-by-ids.js +5 -5
- package/lib/mongodb/queries/find-one-by-id.js +1 -1
- package/lib/mongodb/queries/find-one.js +12 -3
- package/lib/mongodb/queries/find-page.js +12 -0
- package/lib/mongodb/queries/find.js +15 -6
- package/lib/mongodb/queries/index.js +1 -0
- package/lib/mongodb/queries/watch.js +537 -0
- package/lib/mongodb/writes/delete-many.js +20 -11
- package/lib/mongodb/writes/delete-one.js +18 -9
- package/lib/mongodb/writes/find-one-and-delete.js +19 -10
- package/lib/mongodb/writes/find-one-and-replace.js +36 -20
- package/lib/mongodb/writes/find-one-and-update.js +36 -20
- package/lib/mongodb/writes/increment-one.js +16 -7
- package/lib/mongodb/writes/index.js +13 -13
- package/lib/mongodb/writes/insert-batch.js +46 -37
- package/lib/mongodb/writes/insert-many.js +22 -13
- package/lib/mongodb/writes/insert-one.js +18 -9
- package/lib/mongodb/writes/replace-one.js +33 -17
- package/lib/mongodb/writes/result-handler.js +14 -14
- package/lib/mongodb/writes/update-many.js +34 -18
- package/lib/mongodb/writes/update-one.js +33 -17
- package/lib/mongodb/writes/upsert-one.js +25 -9
- package/lib/operators.js +1 -1
- package/lib/redis-cache-adapter.js +3 -3
- package/lib/slow-query-log/base-storage.js +69 -0
- package/lib/slow-query-log/batch-queue.js +96 -0
- package/lib/slow-query-log/config-manager.js +195 -0
- package/lib/slow-query-log/index.js +237 -0
- package/lib/slow-query-log/mongodb-storage.js +323 -0
- package/lib/slow-query-log/query-hash.js +38 -0
- package/lib/transaction/DistributedCacheLockManager.js +240 -5
- package/lib/transaction/Transaction.js +1 -1
- package/lib/utils/objectid-converter.js +566 -0
- package/package.json +11 -5
package/index.d.ts
CHANGED
|
@@ -51,6 +51,95 @@ declare module 'monsqlize' {
|
|
|
51
51
|
// 事务相关类型定义(Transaction API)
|
|
52
52
|
// ============================================================================
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* 业务锁配置选项
|
|
56
|
+
* @since v1.4.0
|
|
57
|
+
*/
|
|
58
|
+
interface LockOptions {
|
|
59
|
+
/** 锁过期时间(毫秒),默认 10000 */
|
|
60
|
+
ttl?: number;
|
|
61
|
+
/** 获取锁失败时的重试次数,默认 3 */
|
|
62
|
+
retryTimes?: number;
|
|
63
|
+
/** 重试间隔(毫秒),默认 100 */
|
|
64
|
+
retryDelay?: number;
|
|
65
|
+
/** Redis 不可用时是否降级为无锁执行,默认 false */
|
|
66
|
+
fallbackToNoLock?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 锁对象
|
|
71
|
+
* 表示一个已获取的锁,提供释放和续期方法
|
|
72
|
+
* @since v1.4.0
|
|
73
|
+
*/
|
|
74
|
+
interface Lock {
|
|
75
|
+
/** 锁的 Key */
|
|
76
|
+
readonly key: string;
|
|
77
|
+
/** 锁的唯一ID */
|
|
78
|
+
readonly lockId: string;
|
|
79
|
+
/** 是否已释放 */
|
|
80
|
+
readonly released: boolean;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 释放锁
|
|
84
|
+
* @returns Promise<boolean> 是否成功释放
|
|
85
|
+
*/
|
|
86
|
+
release(): Promise<boolean>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 续期(延长 TTL)
|
|
90
|
+
* @param ttl - 新的过期时间(毫秒),默认使用原TTL
|
|
91
|
+
* @returns Promise<boolean> 是否成功续期
|
|
92
|
+
*/
|
|
93
|
+
renew(ttl?: number): Promise<boolean>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 检查锁是否仍被持有
|
|
97
|
+
* @returns boolean
|
|
98
|
+
*/
|
|
99
|
+
isHeld(): boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 获取锁持有时间(毫秒)
|
|
103
|
+
* @returns number
|
|
104
|
+
*/
|
|
105
|
+
getHoldTime(): number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 锁统计信息
|
|
110
|
+
* @since v1.4.0
|
|
111
|
+
*/
|
|
112
|
+
interface LockStats {
|
|
113
|
+
/** 成功获取锁的次数 */
|
|
114
|
+
locksAcquired: number;
|
|
115
|
+
/** 成功释放锁的次数 */
|
|
116
|
+
locksReleased: number;
|
|
117
|
+
/** 锁检查次数 */
|
|
118
|
+
lockChecks: number;
|
|
119
|
+
/** 错误次数 */
|
|
120
|
+
errors: number;
|
|
121
|
+
/** 锁键前缀 */
|
|
122
|
+
lockKeyPrefix: string;
|
|
123
|
+
/** 锁最大持续时间 */
|
|
124
|
+
maxDuration: number;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 锁获取失败错误
|
|
129
|
+
* @since v1.4.0
|
|
130
|
+
*/
|
|
131
|
+
class LockAcquireError extends Error {
|
|
132
|
+
readonly code: 'LOCK_ACQUIRE_FAILED';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 锁超时错误
|
|
137
|
+
* @since v1.4.0
|
|
138
|
+
*/
|
|
139
|
+
class LockTimeoutError extends Error {
|
|
140
|
+
readonly code: 'LOCK_TIMEOUT';
|
|
141
|
+
}
|
|
142
|
+
|
|
54
143
|
/**
|
|
55
144
|
* MongoDB 事务会话(原生 ClientSession)
|
|
56
145
|
* @since v0.2.0
|
|
@@ -302,6 +391,16 @@ declare module 'monsqlize' {
|
|
|
302
391
|
wtimeout?: number; // 写超时时间(毫秒)
|
|
303
392
|
}
|
|
304
393
|
|
|
394
|
+
/**
|
|
395
|
+
* 简化的插入选项(用于简化调用形式)
|
|
396
|
+
*/
|
|
397
|
+
interface InsertOneSimplifiedOptions {
|
|
398
|
+
writeConcern?: WriteConcern; // 写确认级别(可选)
|
|
399
|
+
bypassDocumentValidation?: boolean; // 跳过文档验证(可选)
|
|
400
|
+
comment?: string; // 查询注释(用于生产环境日志跟踪)
|
|
401
|
+
session?: any; // 事务会话
|
|
402
|
+
}
|
|
403
|
+
|
|
305
404
|
interface InsertOneOptions {
|
|
306
405
|
document: any; // 要插入的文档
|
|
307
406
|
writeConcern?: WriteConcern; // 写确认级别(可选)
|
|
@@ -314,6 +413,17 @@ declare module 'monsqlize' {
|
|
|
314
413
|
insertedId: any; // 插入的文档 _id
|
|
315
414
|
}
|
|
316
415
|
|
|
416
|
+
/**
|
|
417
|
+
* 简化的批量插入选项(用于简化调用形式)
|
|
418
|
+
*/
|
|
419
|
+
interface InsertManySimplifiedOptions {
|
|
420
|
+
ordered?: boolean; // 是否有序插入(默认 true)
|
|
421
|
+
writeConcern?: WriteConcern; // 写确认级别(可选)
|
|
422
|
+
bypassDocumentValidation?: boolean; // 跳过文档验证(可选)
|
|
423
|
+
comment?: string; // 查询注释(用于生产环境日志跟踪)
|
|
424
|
+
session?: any; // 事务会话
|
|
425
|
+
}
|
|
426
|
+
|
|
317
427
|
interface InsertManyOptions {
|
|
318
428
|
documents: any[]; // 要插入的文档数组
|
|
319
429
|
ordered?: boolean; // 是否有序插入(默认 true)
|
|
@@ -484,16 +594,16 @@ declare module 'monsqlize' {
|
|
|
484
594
|
driver?: { connected: boolean };
|
|
485
595
|
}
|
|
486
596
|
|
|
487
|
-
interface CollectionAccessor {
|
|
597
|
+
interface CollectionAccessor<TSchema = any> {
|
|
488
598
|
getNamespace(): { iid: string; type: string; db: string; collection: string };
|
|
489
599
|
dropCollection(): Promise<boolean>;
|
|
490
600
|
createCollection(name?: string | null, options?: any): Promise<boolean>;
|
|
491
601
|
createView(viewName: string, source: string, pipeline?: any[]): Promise<boolean>;
|
|
492
602
|
|
|
493
|
-
// findOne 重载:支持 meta
|
|
494
|
-
findOne(query?: any, options?: Omit<FindOptions, 'meta'>): Promise<
|
|
495
|
-
findOne(query: any, options: FindOptions & { meta: true | MetaOptions }): Promise<ResultWithMeta<
|
|
496
|
-
findOne(query?: any, options?: FindOptions): Promise<
|
|
603
|
+
// findOne 重载:支持 meta 参数和泛型
|
|
604
|
+
findOne<T = TSchema>(query?: any, options?: Omit<FindOptions, 'meta'>): Promise<T | null>;
|
|
605
|
+
findOne<T = TSchema>(query: any, options: FindOptions & { meta: true | MetaOptions }): Promise<ResultWithMeta<T | null>>;
|
|
606
|
+
findOne<T = TSchema>(query?: any, options?: FindOptions): Promise<T | null | ResultWithMeta<T | null>>;
|
|
497
607
|
|
|
498
608
|
/**
|
|
499
609
|
* 通过 _id 查询单个文档(便利方法)
|
|
@@ -563,9 +673,9 @@ u /**
|
|
|
563
673
|
): Promise<any[]>;
|
|
564
674
|
|
|
565
675
|
// find 重载:支持 meta 参数和链式调用 (v2.0+)
|
|
566
|
-
find<T =
|
|
567
|
-
find<T =
|
|
568
|
-
find<T =
|
|
676
|
+
find<T = TSchema>(query?: any): FindChain<T>;
|
|
677
|
+
find<T = TSchema>(query: any, options: FindOptions & { meta: true | MetaOptions }): Promise<ResultWithMeta<T[]>>;
|
|
678
|
+
find<T = TSchema>(query?: any, options?: FindOptions): Promise<T[]> | FindChain<T> | ResultWithMeta<T[]>;
|
|
569
679
|
|
|
570
680
|
// count 重载:支持 meta 参数
|
|
571
681
|
count(query?: any, options?: Omit<CountOptions, 'meta'>): Promise<number>;
|
|
@@ -573,9 +683,9 @@ u /**
|
|
|
573
683
|
count(query?: any, options?: CountOptions): Promise<number | ResultWithMeta<number>>;
|
|
574
684
|
|
|
575
685
|
// aggregate 重载:支持 meta 参数和链式调用 (v2.0+)
|
|
576
|
-
aggregate<T =
|
|
577
|
-
aggregate<T =
|
|
578
|
-
aggregate<T =
|
|
686
|
+
aggregate<T = TSchema>(pipeline?: any[]): AggregateChain<T>;
|
|
687
|
+
aggregate<T = TSchema>(pipeline: any[], options: AggregateOptions & { meta: true | MetaOptions }): Promise<ResultWithMeta<T[]>>;
|
|
688
|
+
aggregate<T = TSchema>(pipeline?: any[], options?: AggregateOptions): Promise<T[]> | AggregateChain<T> | ResultWithMeta<T[]>;
|
|
579
689
|
|
|
580
690
|
// distinct 重载:支持 meta 参数
|
|
581
691
|
distinct<T = any>(field: string, query?: any, options?: Omit<DistinctOptions, 'meta'>): Promise<T[]>;
|
|
@@ -594,10 +704,13 @@ u /**
|
|
|
594
704
|
clearBookmarks(keyDims?: BookmarkKeyDims): Promise<ClearBookmarksResult>;
|
|
595
705
|
|
|
596
706
|
// findPage:已在 PageResult 中包含 meta 字段,无需重载
|
|
597
|
-
findPage(options: FindPageOptions): Promise<PageResult
|
|
707
|
+
findPage<T = TSchema>(options: FindPageOptions): Promise<PageResult<T>>;
|
|
598
708
|
|
|
599
|
-
// 写入操作
|
|
709
|
+
// 写入操作 - 支持简化调用和完整配置
|
|
710
|
+
insertOne<T = TSchema>(document: T, options?: InsertOneSimplifiedOptions): Promise<InsertOneResult>;
|
|
600
711
|
insertOne(options: InsertOneOptions): Promise<InsertOneResult>;
|
|
712
|
+
|
|
713
|
+
insertMany<T = TSchema>(documents: T[], options?: InsertManySimplifiedOptions): Promise<InsertManyResult>;
|
|
601
714
|
insertMany(options: InsertManyOptions): Promise<InsertManyResult>;
|
|
602
715
|
|
|
603
716
|
/**
|
|
@@ -672,8 +785,8 @@ u /**
|
|
|
672
785
|
}
|
|
673
786
|
|
|
674
787
|
type DbAccessor = {
|
|
675
|
-
collection
|
|
676
|
-
db
|
|
788
|
+
collection<TSchema = any>(name: string): CollectionAccessor<TSchema>;
|
|
789
|
+
db(dbName: string): { collection<TSchema = any>(name: string): CollectionAccessor<TSchema> };
|
|
677
790
|
};
|
|
678
791
|
|
|
679
792
|
export default class MonSQLize {
|
|
@@ -713,6 +826,46 @@ u /**
|
|
|
713
826
|
/** 健康检查 */
|
|
714
827
|
health(): Promise<HealthView>;
|
|
715
828
|
|
|
829
|
+
/**
|
|
830
|
+
* 获取慢查询日志(持久化存储)
|
|
831
|
+
* @param filter - 查询条件(可选)
|
|
832
|
+
* @param options - 查询选项(可选)
|
|
833
|
+
* @returns 慢查询日志数组
|
|
834
|
+
* @since v1.3.1
|
|
835
|
+
* @example
|
|
836
|
+
* // 查询所有慢查询
|
|
837
|
+
* const logs = await msq.getSlowQueryLogs({}, { limit: 10 });
|
|
838
|
+
*
|
|
839
|
+
* // 按collection过滤
|
|
840
|
+
* const userLogs = await msq.getSlowQueryLogs(
|
|
841
|
+
* { collection: 'users' },
|
|
842
|
+
* { sort: { count: -1 }, limit: 10 }
|
|
843
|
+
* );
|
|
844
|
+
*
|
|
845
|
+
* // 按操作类型过滤
|
|
846
|
+
* const findLogs = await msq.getSlowQueryLogs({ operation: 'find' });
|
|
847
|
+
*/
|
|
848
|
+
getSlowQueryLogs(
|
|
849
|
+
filter?: Record<string, any>,
|
|
850
|
+
options?: {
|
|
851
|
+
sort?: Record<string, 1 | -1>;
|
|
852
|
+
limit?: number;
|
|
853
|
+
skip?: number;
|
|
854
|
+
}
|
|
855
|
+
): Promise<Array<{
|
|
856
|
+
queryHash: string;
|
|
857
|
+
database: string;
|
|
858
|
+
collection: string;
|
|
859
|
+
operation: string;
|
|
860
|
+
count: number;
|
|
861
|
+
totalTimeMs: number;
|
|
862
|
+
avgTimeMs: number;
|
|
863
|
+
maxTimeMs: number;
|
|
864
|
+
minTimeMs: number;
|
|
865
|
+
firstSeen: Date;
|
|
866
|
+
lastSeen: Date;
|
|
867
|
+
}>>;
|
|
868
|
+
|
|
716
869
|
/**
|
|
717
870
|
* 创建手动事务会话
|
|
718
871
|
* @param options - 事务选项
|
|
@@ -745,6 +898,90 @@ u /**
|
|
|
745
898
|
callback: (transaction: Transaction) => Promise<T>,
|
|
746
899
|
options?: TransactionOptions
|
|
747
900
|
): Promise<T>;
|
|
901
|
+
|
|
902
|
+
// ============================================================================
|
|
903
|
+
// 业务锁 API (v1.4.0+)
|
|
904
|
+
// ============================================================================
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* 业务锁:自动管理锁生命周期(推荐)
|
|
908
|
+
* @param key - 锁的唯一标识
|
|
909
|
+
* @param callback - 获取锁后执行的函数
|
|
910
|
+
* @param options - 锁选项
|
|
911
|
+
* @returns callback 的返回值
|
|
912
|
+
* @since v1.4.0
|
|
913
|
+
* @example
|
|
914
|
+
* // 库存扣减
|
|
915
|
+
* await db.withLock('inventory:SKU123', async () => {
|
|
916
|
+
* const product = await inventory.findOne({ sku: 'SKU123' });
|
|
917
|
+
* if (product.stock >= 1) {
|
|
918
|
+
* await inventory.updateOne({ sku: 'SKU123' }, { $inc: { stock: -1 } });
|
|
919
|
+
* }
|
|
920
|
+
* });
|
|
921
|
+
*/
|
|
922
|
+
withLock?<T = any>(
|
|
923
|
+
key: string,
|
|
924
|
+
callback: () => Promise<T>,
|
|
925
|
+
options?: LockOptions
|
|
926
|
+
): Promise<T>;
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* 业务锁:手动获取锁(阻塞重试)
|
|
930
|
+
* @param key - 锁的唯一标识
|
|
931
|
+
* @param options - 锁选项
|
|
932
|
+
* @returns Lock 对象
|
|
933
|
+
* @since v1.4.0
|
|
934
|
+
* @example
|
|
935
|
+
* const lock = await db.acquireLock('resource:123', { ttl: 5000 });
|
|
936
|
+
* try {
|
|
937
|
+
* // 业务逻辑
|
|
938
|
+
* } finally {
|
|
939
|
+
* await lock.release();
|
|
940
|
+
* }
|
|
941
|
+
*/
|
|
942
|
+
acquireLock?(key: string, options?: LockOptions): Promise<Lock>;
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* 业务锁:尝试获取锁(不阻塞)
|
|
946
|
+
* @param key - 锁的唯一标识
|
|
947
|
+
* @param options - 锁选项(不包含 retryTimes)
|
|
948
|
+
* @returns Lock 对象或 null
|
|
949
|
+
* @since v1.4.0
|
|
950
|
+
* @example
|
|
951
|
+
* const lock = await db.tryAcquireLock('resource:123');
|
|
952
|
+
* if (lock) {
|
|
953
|
+
* try {
|
|
954
|
+
* // 业务逻辑
|
|
955
|
+
* } finally {
|
|
956
|
+
* await lock.release();
|
|
957
|
+
* }
|
|
958
|
+
* } else {
|
|
959
|
+
* console.log('资源被占用');
|
|
960
|
+
* }
|
|
961
|
+
*/
|
|
962
|
+
tryAcquireLock?(key: string, options?: Omit<LockOptions, 'retryTimes'>): Promise<Lock | null>;
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* 获取锁统计信息
|
|
966
|
+
* @returns 锁统计信息
|
|
967
|
+
* @since v1.4.0
|
|
968
|
+
* @example
|
|
969
|
+
* const stats = db.getLockStats();
|
|
970
|
+
* console.log(stats.locksAcquired, stats.locksReleased);
|
|
971
|
+
*/
|
|
972
|
+
getLockStats?(): LockStats;
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* 创建 Redis 缓存适配器(静态方法)
|
|
976
|
+
* @param client - Redis 客户端实例(ioredis)
|
|
977
|
+
* @param options - 可选配置
|
|
978
|
+
* @returns Redis 缓存适配器实例
|
|
979
|
+
* @example
|
|
980
|
+
* const Redis = require('ioredis');
|
|
981
|
+
* const redis = new Redis();
|
|
982
|
+
* const redisCache = MonSQLize.createRedisCacheAdapter(redis);
|
|
983
|
+
*/
|
|
984
|
+
static createRedisCacheAdapter(client: any, options?: any): CacheLike;
|
|
748
985
|
}
|
|
749
986
|
|
|
750
987
|
// ============================================================================
|
package/lib/cache.js
CHANGED
|
@@ -13,7 +13,7 @@ class Cache {
|
|
|
13
13
|
enableStats: options.enableStats !== false, // 启用统计信息
|
|
14
14
|
...options
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
// 统计信息
|
|
18
18
|
this.stats = {
|
|
19
19
|
hits: 0,
|
|
@@ -141,13 +141,13 @@ class Cache {
|
|
|
141
141
|
async delPattern(pattern) {
|
|
142
142
|
const regex = this._patternToRegex(pattern);
|
|
143
143
|
const keysToDelete = [];
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
for (const key of this.cache.keys()) {
|
|
146
146
|
if (regex.test(key)) {
|
|
147
147
|
keysToDelete.push(key);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
|
|
152
152
|
return await this.delMany(keysToDelete);
|
|
153
153
|
}
|
|
@@ -231,7 +231,7 @@ class Cache {
|
|
|
231
231
|
_estimateSize(key, value) {
|
|
232
232
|
const keySize = typeof key === 'string' ? key.length * 2 : 8;
|
|
233
233
|
let valueSize = 8; // 默认值
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
if (typeof value === 'string') {
|
|
236
236
|
valueSize = value.length * 2;
|
|
237
237
|
} else if (typeof value === 'object' && value !== null) {
|
|
@@ -241,7 +241,7 @@ class Cache {
|
|
|
241
241
|
valueSize = 100; // 估算值
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
-
|
|
244
|
+
|
|
245
245
|
return keySize + valueSize;
|
|
246
246
|
}
|
|
247
247
|
|
|
@@ -471,7 +471,7 @@ module.exports = class CacheFactory {
|
|
|
471
471
|
return (op, base = {}, fetcher) => {
|
|
472
472
|
// 检查是否在事务中
|
|
473
473
|
const inTransaction = base.session && base.session.__monSQLizeTransaction;
|
|
474
|
-
|
|
474
|
+
|
|
475
475
|
// 事务内默认不缓存(除非显式指定 cache)
|
|
476
476
|
let ttl = 0;
|
|
477
477
|
if (inTransaction) {
|
|
@@ -481,11 +481,11 @@ module.exports = class CacheFactory {
|
|
|
481
481
|
// 非事务:正常处理 cache 参数
|
|
482
482
|
ttl = base.cache ? Number(base.cache) : 0;
|
|
483
483
|
}
|
|
484
|
-
|
|
484
|
+
|
|
485
485
|
// 使用浅拷贝构建用于键的对象,避免修改调用方入参
|
|
486
486
|
const { cache: _cacheTTL, maxTimeMS: _maxTimeMS, session: _session, ...keyBase } = base || {};
|
|
487
487
|
const key = this.buildCacheKey({ ...ctx, op, base: keyBase });
|
|
488
488
|
return this.readThrough(cache, ttl, key, fetcher);
|
|
489
489
|
};
|
|
490
490
|
}
|
|
491
|
-
}
|
|
491
|
+
};
|
package/lib/common/validation.js
CHANGED
|
@@ -46,4 +46,67 @@ function assertCursorSortCompatible(currentSort, cursorSort) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* 验证数值范围
|
|
51
|
+
* @param {number} value - 要验证的数值
|
|
52
|
+
* @param {number} min - 最小值(包含)
|
|
53
|
+
* @param {number} max - 最大值(包含)
|
|
54
|
+
* @param {string} name - 参数名称(用于错误消息)
|
|
55
|
+
* @returns {number} 验证通过后的值
|
|
56
|
+
* @throws {Error} 当值不在有效范围内时抛出 INVALID_ARGUMENT 错误
|
|
57
|
+
*/
|
|
58
|
+
function validateRange(value, min, max, name) {
|
|
59
|
+
const { ErrorCodes, createError } = require('../errors');
|
|
60
|
+
|
|
61
|
+
// 检查是否为有效数字
|
|
62
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
63
|
+
throw createError(
|
|
64
|
+
ErrorCodes.INVALID_ARGUMENT,
|
|
65
|
+
`${name} 必须是一个有效的数字`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 检查是否为有限数(排除 Infinity)
|
|
70
|
+
if (!isFinite(value)) {
|
|
71
|
+
throw createError(
|
|
72
|
+
ErrorCodes.INVALID_ARGUMENT,
|
|
73
|
+
`${name} 必须是有限数字`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 检查范围
|
|
78
|
+
if (value < min || value > max) {
|
|
79
|
+
throw createError(
|
|
80
|
+
ErrorCodes.INVALID_ARGUMENT,
|
|
81
|
+
`${name} 必须在 ${min} 到 ${max} 之间,当前值: ${value}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 验证正整数
|
|
90
|
+
* @param {number} value - 要验证的数值
|
|
91
|
+
* @param {string} name - 参数名称(用于错误消息)
|
|
92
|
+
* @returns {number} 验证通过后的值
|
|
93
|
+
* @throws {Error} 当值不是正整数时抛出错误
|
|
94
|
+
*/
|
|
95
|
+
function validatePositiveInteger(value, name) {
|
|
96
|
+
const { ErrorCodes, createError } = require('../errors');
|
|
97
|
+
|
|
98
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
99
|
+
throw createError(
|
|
100
|
+
ErrorCodes.INVALID_ARGUMENT,
|
|
101
|
+
`${name} 必须是正整数,当前值: ${value}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
validateLimitAfterBefore,
|
|
109
|
+
assertCursorSortCompatible,
|
|
110
|
+
validateRange,
|
|
111
|
+
validatePositiveInteger
|
|
112
|
+
};
|
package/lib/connect.js
CHANGED
package/lib/errors.js
CHANGED
|
@@ -43,6 +43,10 @@ const ErrorCodes = {
|
|
|
43
43
|
DOCUMENTS_REQUIRED: 'DOCUMENTS_REQUIRED',
|
|
44
44
|
DUPLICATE_KEY: 'DUPLICATE_KEY',
|
|
45
45
|
WRITE_CONFLICT: 'WRITE_CONFLICT',
|
|
46
|
+
|
|
47
|
+
// 🆕 v1.4.0: 锁相关错误
|
|
48
|
+
LOCK_ACQUIRE_FAILED: 'LOCK_ACQUIRE_FAILED',
|
|
49
|
+
LOCK_TIMEOUT: 'LOCK_TIMEOUT',
|
|
46
50
|
};
|
|
47
51
|
|
|
48
52
|
/**
|
|
@@ -145,6 +149,9 @@ function createWriteError(operation, message, cause = null) {
|
|
|
145
149
|
);
|
|
146
150
|
}
|
|
147
151
|
|
|
152
|
+
// 🆕 v1.4.0: 导入锁错误类
|
|
153
|
+
const { LockAcquireError, LockTimeoutError } = require('./lock/errors');
|
|
154
|
+
|
|
148
155
|
module.exports = {
|
|
149
156
|
ErrorCodes,
|
|
150
157
|
createError,
|
|
@@ -153,5 +160,8 @@ module.exports = {
|
|
|
153
160
|
createConnectionError,
|
|
154
161
|
createQueryTimeoutError,
|
|
155
162
|
createWriteError,
|
|
163
|
+
// 🆕 v1.4.0: 锁错误类
|
|
164
|
+
LockAcquireError,
|
|
165
|
+
LockTimeoutError,
|
|
156
166
|
};
|
|
157
167
|
|