@futdevpro/fsm-dynamo 1.16.9 → 1.16.10
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/build/_models/control-models/data-model-params.control-model.d.ts +41 -0
- package/build/_models/control-models/data-model-params.control-model.d.ts.map +1 -1
- package/build/_models/control-models/data-model-params.control-model.js +26 -0
- package/build/_models/control-models/data-model-params.control-model.js.map +1 -1
- package/package.json +1 -1
- package/src/_models/control-models/data-model-params.control-model.spec.ts +49 -1
- package/src/_models/control-models/data-model-params.control-model.ts +52 -0
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
import { DyFM_DataModel_Type } from '../../_enums/data-model-type.enum';
|
|
2
2
|
import { DyFM_Metadata } from '../data-models/metadata.data-model';
|
|
3
3
|
import { DyFM_DataProperties, DyFM_DataProperty_Params } from './data-property-params.control-model';
|
|
4
|
+
/**
|
|
5
|
+
* FR-258 / SR-1 — declarative automatic document retention.
|
|
6
|
+
*
|
|
7
|
+
* When a data-model declares `retention`, `DyNTS_App` startup installs a MongoDB TTL index on
|
|
8
|
+
* the record's `__created` Date field, so MongoDB NATIVELY auto-deletes documents older than the
|
|
9
|
+
* configured age. This is the fleet-wide defense against the unbounded-collection-growth class:
|
|
10
|
+
* a collection that grows without bound (logs / history / events / metrics / reports / archives)
|
|
11
|
+
* and is then loaded by an unbounded find pulls 100s of MB into heap → GC-thrash → OOM. TTL keeps
|
|
12
|
+
* the collection bounded with ZERO app-level loading.
|
|
13
|
+
*
|
|
14
|
+
* Declare EITHER `retentionDays` OR `ttlSeconds` (`ttlSeconds` wins if both are set).
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const params = new DyFM_DataModel_Params<MyLog>({
|
|
19
|
+
* dataName: 'my_log',
|
|
20
|
+
* retention: { retentionDays: 14 }, // auto-delete 14 days after creation
|
|
21
|
+
* properties: { ... },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export interface DyFM_RetentionSettings {
|
|
26
|
+
/** Auto-delete documents this many days after their `__created` timestamp. */
|
|
27
|
+
retentionDays?: number;
|
|
28
|
+
/** Explicit TTL in seconds; takes precedence over `retentionDays`. */
|
|
29
|
+
ttlSeconds?: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a {@link DyFM_RetentionSettings} to a positive integer TTL in seconds, or `undefined`
|
|
33
|
+
* when no valid retention is configured (→ no TTL index is installed). `ttlSeconds` wins over
|
|
34
|
+
* `retentionDays`; non-finite or ≤ 0 values are rejected. Pure + side-effect-free (unit-tested).
|
|
35
|
+
*/
|
|
36
|
+
export declare function DyFM_resolveRetentionTtlSeconds(retention?: DyFM_RetentionSettings): number | undefined;
|
|
4
37
|
export declare class DyFM_DataModel_Settings<T extends DyFM_Metadata> extends DyFM_Metadata {
|
|
5
38
|
/** this will be used as db table name, therefore it should be in snake_case,
|
|
6
39
|
* this also will be used in logs and errors
|
|
@@ -20,6 +53,14 @@ export declare class DyFM_DataModel_Settings<T extends DyFM_Metadata> extends Dy
|
|
|
20
53
|
* @default false
|
|
21
54
|
*/
|
|
22
55
|
forbidDelete?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* FR-258 / SR-1 — declarative automatic retention. If set, `DyNTS_App` startup creates a
|
|
58
|
+
* MongoDB TTL index on `__created`, so MongoDB auto-deletes documents older than the configured
|
|
59
|
+
* age (no app-level load → prevents the unbounded-growth → OOM class). See {@link DyFM_RetentionSettings}.
|
|
60
|
+
*
|
|
61
|
+
* @example { retentionDays: 14 }
|
|
62
|
+
*/
|
|
63
|
+
retention?: DyFM_RetentionSettings;
|
|
23
64
|
/**
|
|
24
65
|
* from these settings we will set the data model properties for the DB.
|
|
25
66
|
* metadata properties will be added automatically to the data model.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-model-params.control-model.d.ts","sourceRoot":"","sources":["../../../src/_models/control-models/data-model-params.control-model.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"data-model-params.control-model.d.ts","sourceRoot":"","sources":["../../../src/_models/control-models/data-model-params.control-model.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAGrG;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,+BAA+B,CAAC,SAAS,CAAC,EAAE,sBAAsB,GAAG,MAAM,GAAG,SAAS,CAQtG;AAID,qBAAa,uBAAuB,CAAC,CAAC,SAAS,aAAa,CAAE,SAAQ,aAAa;IACjF;;OAEG;IACH,QAAQ,EAAG,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAEhC,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;IAEnC;;;OAGG;IACH,UAAU,EAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;CACrC;AAcD,qBAAa,qBAAqB,CAAC,CAAC,SAAS,aAAa,CAAE,SAAQ,uBAAuB,CAAC,CAAC,CAAC;IAG5F,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG9B,GAAG,EAAE,uBAAuB,CAAC,CAAC,CAAC;IAiNjC,WAAW,CAAC,IAAI,OAAO;IAIvB,yBAAyB,CAAC,CAAC,QAAQ,EAAE,wBAAwB,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI;CA0B7E"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DyFM_DataModel_Params = exports.DyFM_DataModel_Settings = void 0;
|
|
4
|
+
exports.DyFM_resolveRetentionTtlSeconds = DyFM_resolveRetentionTtlSeconds;
|
|
4
5
|
const log_util_1 = require("../../_collections/utils/log.util");
|
|
5
6
|
const object_util_1 = require("../../_collections/utils/object.util");
|
|
6
7
|
const stack_util_1 = require("../../_collections/utils/stack.util");
|
|
@@ -10,6 +11,23 @@ const data_model_type_enum_1 = require("../../_enums/data-model-type.enum");
|
|
|
10
11
|
const metadata_data_model_1 = require("../data-models/metadata.data-model");
|
|
11
12
|
const data_property_params_control_model_1 = require("./data-property-params.control-model");
|
|
12
13
|
const error_control_model_1 = require("./error.control-model");
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a {@link DyFM_RetentionSettings} to a positive integer TTL in seconds, or `undefined`
|
|
16
|
+
* when no valid retention is configured (→ no TTL index is installed). `ttlSeconds` wins over
|
|
17
|
+
* `retentionDays`; non-finite or ≤ 0 values are rejected. Pure + side-effect-free (unit-tested).
|
|
18
|
+
*/
|
|
19
|
+
function DyFM_resolveRetentionTtlSeconds(retention) {
|
|
20
|
+
if (!retention) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
const raw = retention.ttlSeconds !== undefined ? retention.ttlSeconds
|
|
24
|
+
: retention.retentionDays !== undefined ? retention.retentionDays * 86400
|
|
25
|
+
: undefined;
|
|
26
|
+
if (raw === undefined || !Number.isFinite(raw) || raw <= 0) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return Math.floor(raw);
|
|
30
|
+
}
|
|
13
31
|
/* export type DyFM_Data_S<T extends DyFM_Metadata> = DyFM_DataModel_Settings<T>;
|
|
14
32
|
export type DyFM_Data_Settings<T extends DyFM_Metadata> = DyFM_DataModel_Settings<T>; */
|
|
15
33
|
class DyFM_DataModel_Settings extends metadata_data_model_1.DyFM_Metadata {
|
|
@@ -31,6 +49,14 @@ class DyFM_DataModel_Settings extends metadata_data_model_1.DyFM_Metadata {
|
|
|
31
49
|
* @default false
|
|
32
50
|
*/
|
|
33
51
|
forbidDelete;
|
|
52
|
+
/**
|
|
53
|
+
* FR-258 / SR-1 — declarative automatic retention. If set, `DyNTS_App` startup creates a
|
|
54
|
+
* MongoDB TTL index on `__created`, so MongoDB auto-deletes documents older than the configured
|
|
55
|
+
* age (no app-level load → prevents the unbounded-growth → OOM class). See {@link DyFM_RetentionSettings}.
|
|
56
|
+
*
|
|
57
|
+
* @example { retentionDays: 14 }
|
|
58
|
+
*/
|
|
59
|
+
retention;
|
|
34
60
|
/**
|
|
35
61
|
* from these settings we will set the data model properties for the DB.
|
|
36
62
|
* metadata properties will be added automatically to the data model.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-model-params.control-model.js","sourceRoot":"","sources":["../../../src/_models/control-models/data-model-params.control-model.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"data-model-params.control-model.js","sourceRoot":"","sources":["../../../src/_models/control-models/data-model-params.control-model.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/fsm-dynamo",
|
|
3
|
-
"version": "01.16.
|
|
3
|
+
"version": "01.16.10",
|
|
4
4
|
"description": "Full Stack Model Collection for Dynamic (NodeJS-Typescript) Framework called Dynamo, by Future Development Ltd.",
|
|
5
5
|
"DyBu_settings": {
|
|
6
6
|
"packageType": "full-stack-package",
|
|
@@ -2,7 +2,7 @@ import { DyFM_Log } from '../../_collections/utils/log.util';
|
|
|
2
2
|
import { DyFM_BasicProperty_Type } from '../../_enums/basic-property-type.enum';
|
|
3
3
|
import { DyFM_DataModel_Type } from '../../_enums/data-model-type.enum';
|
|
4
4
|
import { DyFM_Metadata } from '../data-models/metadata.data-model';
|
|
5
|
-
import { DyFM_DataModel_Params, DyFM_DataModel_Settings } from './data-model-params.control-model';
|
|
5
|
+
import { DyFM_DataModel_Params, DyFM_DataModel_Settings, DyFM_resolveRetentionTtlSeconds } from './data-model-params.control-model';
|
|
6
6
|
import { DyFM_DataProperties } from './data-property-params.control-model';
|
|
7
7
|
import { DyFM_Error } from './error.control-model';
|
|
8
8
|
|
|
@@ -82,4 +82,52 @@ describe('| DyFM_DataModel_Params', () => {
|
|
|
82
82
|
const params = new DyFM_DataModel_Params(mockSettings);
|
|
83
83
|
expect(params.constructed()).toBeTrue();
|
|
84
84
|
});
|
|
85
|
+
|
|
86
|
+
// FR-258 / SR-1 — the optional `retention` declaration must survive construction so
|
|
87
|
+
// DyNTS_App startup can read it to install the TTL index.
|
|
88
|
+
it('| should carry the optional `retention` declaration through to the params instance', () => {
|
|
89
|
+
mockSettings.retention = { retentionDays: 14 };
|
|
90
|
+
const params = new DyFM_DataModel_Params(mockSettings);
|
|
91
|
+
expect(params.retention).toEqual({ retentionDays: 14 });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('| should leave `retention` undefined when not declared (backward-compatible)', () => {
|
|
95
|
+
const params = new DyFM_DataModel_Params(mockSettings);
|
|
96
|
+
expect(params.retention).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// FR-258 / SR-1 — pure resolver: retention declaration → positive integer TTL seconds (or undefined).
|
|
101
|
+
describe('| DyFM_resolveRetentionTtlSeconds (FR-258 / SR-1)', () => {
|
|
102
|
+
it('| undefined retention → undefined (no TTL index)', () => {
|
|
103
|
+
expect(DyFM_resolveRetentionTtlSeconds(undefined)).toBeUndefined();
|
|
104
|
+
expect(DyFM_resolveRetentionTtlSeconds({})).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('| retentionDays → days * 86400', () => {
|
|
108
|
+
expect(DyFM_resolveRetentionTtlSeconds({ retentionDays: 14 })).toBe(14 * 86400);
|
|
109
|
+
expect(DyFM_resolveRetentionTtlSeconds({ retentionDays: 1 })).toBe(86400);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('| ttlSeconds → passed through as-is', () => {
|
|
113
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: 3600 })).toBe(3600);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('| ttlSeconds takes precedence over retentionDays when both set', () => {
|
|
117
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: 100, retentionDays: 30 })).toBe(100);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('| rejects zero / negative / non-finite (→ undefined, no index)', () => {
|
|
121
|
+
expect(DyFM_resolveRetentionTtlSeconds({ retentionDays: 0 })).toBeUndefined();
|
|
122
|
+
expect(DyFM_resolveRetentionTtlSeconds({ retentionDays: -5 })).toBeUndefined();
|
|
123
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: 0 })).toBeUndefined();
|
|
124
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: -1 })).toBeUndefined();
|
|
125
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: Number.NaN })).toBeUndefined();
|
|
126
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: Number.POSITIVE_INFINITY })).toBeUndefined();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('| floors fractional results to a whole number of seconds', () => {
|
|
130
|
+
expect(DyFM_resolveRetentionTtlSeconds({ ttlSeconds: 99.9 })).toBe(99);
|
|
131
|
+
expect(DyFM_resolveRetentionTtlSeconds({ retentionDays: 0.5 })).toBe(43200);
|
|
132
|
+
});
|
|
85
133
|
});
|
|
@@ -8,6 +8,49 @@ import { DyFM_Metadata } from '../data-models/metadata.data-model';
|
|
|
8
8
|
import { DyFM_DataProperties, DyFM_DataProperty_Params } from './data-property-params.control-model';
|
|
9
9
|
import { DyFM_Error } from './error.control-model';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* FR-258 / SR-1 — declarative automatic document retention.
|
|
13
|
+
*
|
|
14
|
+
* When a data-model declares `retention`, `DyNTS_App` startup installs a MongoDB TTL index on
|
|
15
|
+
* the record's `__created` Date field, so MongoDB NATIVELY auto-deletes documents older than the
|
|
16
|
+
* configured age. This is the fleet-wide defense against the unbounded-collection-growth class:
|
|
17
|
+
* a collection that grows without bound (logs / history / events / metrics / reports / archives)
|
|
18
|
+
* and is then loaded by an unbounded find pulls 100s of MB into heap → GC-thrash → OOM. TTL keeps
|
|
19
|
+
* the collection bounded with ZERO app-level loading.
|
|
20
|
+
*
|
|
21
|
+
* Declare EITHER `retentionDays` OR `ttlSeconds` (`ttlSeconds` wins if both are set).
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const params = new DyFM_DataModel_Params<MyLog>({
|
|
26
|
+
* dataName: 'my_log',
|
|
27
|
+
* retention: { retentionDays: 14 }, // auto-delete 14 days after creation
|
|
28
|
+
* properties: { ... },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export interface DyFM_RetentionSettings {
|
|
33
|
+
/** Auto-delete documents this many days after their `__created` timestamp. */
|
|
34
|
+
retentionDays?: number;
|
|
35
|
+
/** Explicit TTL in seconds; takes precedence over `retentionDays`. */
|
|
36
|
+
ttlSeconds?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Resolve a {@link DyFM_RetentionSettings} to a positive integer TTL in seconds, or `undefined`
|
|
41
|
+
* when no valid retention is configured (→ no TTL index is installed). `ttlSeconds` wins over
|
|
42
|
+
* `retentionDays`; non-finite or ≤ 0 values are rejected. Pure + side-effect-free (unit-tested).
|
|
43
|
+
*/
|
|
44
|
+
export function DyFM_resolveRetentionTtlSeconds(retention?: DyFM_RetentionSettings): number | undefined {
|
|
45
|
+
if (!retention) { return undefined; }
|
|
46
|
+
const raw: number | undefined =
|
|
47
|
+
retention.ttlSeconds !== undefined ? retention.ttlSeconds
|
|
48
|
+
: retention.retentionDays !== undefined ? retention.retentionDays * 86400
|
|
49
|
+
: undefined;
|
|
50
|
+
if (raw === undefined || !Number.isFinite(raw) || raw <= 0) { return undefined; }
|
|
51
|
+
return Math.floor(raw);
|
|
52
|
+
}
|
|
53
|
+
|
|
11
54
|
/* export type DyFM_Data_S<T extends DyFM_Metadata> = DyFM_DataModel_Settings<T>;
|
|
12
55
|
export type DyFM_Data_Settings<T extends DyFM_Metadata> = DyFM_DataModel_Settings<T>; */
|
|
13
56
|
export class DyFM_DataModel_Settings<T extends DyFM_Metadata> extends DyFM_Metadata {
|
|
@@ -33,6 +76,15 @@ export class DyFM_DataModel_Settings<T extends DyFM_Metadata> extends DyFM_Metad
|
|
|
33
76
|
*/
|
|
34
77
|
forbidDelete?: boolean;
|
|
35
78
|
|
|
79
|
+
/**
|
|
80
|
+
* FR-258 / SR-1 — declarative automatic retention. If set, `DyNTS_App` startup creates a
|
|
81
|
+
* MongoDB TTL index on `__created`, so MongoDB auto-deletes documents older than the configured
|
|
82
|
+
* age (no app-level load → prevents the unbounded-growth → OOM class). See {@link DyFM_RetentionSettings}.
|
|
83
|
+
*
|
|
84
|
+
* @example { retentionDays: 14 }
|
|
85
|
+
*/
|
|
86
|
+
retention?: DyFM_RetentionSettings;
|
|
87
|
+
|
|
36
88
|
/**
|
|
37
89
|
* from these settings we will set the data model properties for the DB.
|
|
38
90
|
* metadata properties will be added automatically to the data model.
|