@toa.io/extensions.exposition 1.0.0-alpha.269 → 1.0.0-alpha.270
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 +31 -0
- package/components/exposition.stash/manifest.toa.yaml +13 -4
- package/components/identity.passkeys/operations/tsconfig.tsbuildinfo +1 -1
- package/documentation/io.md +26 -23
- package/documentation/notes/throttling.md +39 -5
- package/features/etag.feature +55 -5
- package/features/io.throttle.feature +3 -5
- package/package.json +5 -5
- package/schemas/io/throttle.cos.yaml +2 -3
- package/source/Directive.test.ts +3 -0
- package/source/Directive.ts +25 -4
- package/source/Endpoint.ts +7 -11
- package/source/Factory.ts +16 -1
- package/source/HTTP/Context.ts +39 -8
- package/source/HTTP/Server.ts +16 -11
- package/source/HTTP/exceptions.ts +9 -3
- package/source/HTTP/messages.ts +31 -0
- package/source/RTD/Directives.ts +6 -0
- package/source/directives/auth/Authorization.ts +29 -3
- package/source/directives/io/Directive.ts +5 -4
- package/source/directives/io/IO.ts +15 -10
- package/source/directives/io/Input.ts +3 -3
- package/source/directives/io/Output.ts +12 -4
- package/source/directives/io/Throttle.ts +9 -12
- package/source/directives/io/lib/throttle/Configuration.test.ts +1 -2
- package/source/directives/io/lib/throttle/Configuration.ts +2 -4
- package/source/directives/io/lib/throttle/Quotas.test.ts +233 -148
- package/source/directives/io/lib/throttle/Quotas.ts +129 -53
- package/source/directives/io/lib/throttle/Sync.test.ts +153 -0
- package/source/directives/io/lib/throttle/Sync.ts +99 -0
- package/source/directives/io/lib/throttle/index.ts +1 -0
- package/source/directives/io/schemas.test.ts +7 -1
- package/transpiled/Directive.d.ts +2 -0
- package/transpiled/Directive.js +16 -4
- package/transpiled/Directive.js.map +1 -1
- package/transpiled/Endpoint.js +7 -8
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Factory.js +14 -1
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/HTTP/Context.d.ts +1 -1
- package/transpiled/HTTP/Context.js +30 -8
- package/transpiled/HTTP/Context.js.map +1 -1
- package/transpiled/HTTP/Server.js +14 -10
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/HTTP/exceptions.d.ts +5 -2
- package/transpiled/HTTP/exceptions.js +7 -3
- package/transpiled/HTTP/exceptions.js.map +1 -1
- package/transpiled/HTTP/messages.d.ts +2 -0
- package/transpiled/HTTP/messages.js +21 -0
- package/transpiled/HTTP/messages.js.map +1 -1
- package/transpiled/RTD/Directives.d.ts +5 -0
- package/transpiled/directives/auth/Authorization.d.ts +1 -0
- package/transpiled/directives/auth/Authorization.js +21 -2
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/io/Directive.d.ts +4 -4
- package/transpiled/directives/io/IO.d.ts +9 -4
- package/transpiled/directives/io/IO.js +14 -9
- package/transpiled/directives/io/IO.js.map +1 -1
- package/transpiled/directives/io/Input.d.ts +1 -1
- package/transpiled/directives/io/Input.js +3 -3
- package/transpiled/directives/io/Input.js.map +1 -1
- package/transpiled/directives/io/Output.d.ts +2 -0
- package/transpiled/directives/io/Output.js +9 -3
- package/transpiled/directives/io/Output.js.map +1 -1
- package/transpiled/directives/io/Throttle.d.ts +3 -5
- package/transpiled/directives/io/Throttle.js +8 -10
- package/transpiled/directives/io/Throttle.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Configuration.d.ts +0 -1
- package/transpiled/directives/io/lib/throttle/Configuration.js +2 -3
- package/transpiled/directives/io/lib/throttle/Configuration.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Quotas.d.ts +46 -22
- package/transpiled/directives/io/lib/throttle/Quotas.js +107 -46
- package/transpiled/directives/io/lib/throttle/Quotas.js.map +1 -1
- package/transpiled/directives/io/lib/throttle/Sync.d.ts +30 -0
- package/transpiled/directives/io/lib/throttle/Sync.js +76 -0
- package/transpiled/directives/io/lib/throttle/Sync.js.map +1 -0
- package/transpiled/directives/io/lib/throttle/index.d.ts +1 -0
- package/transpiled/directives/io/lib/throttle/index.js +3 -1
- package/transpiled/directives/io/lib/throttle/index.js.map +1 -1
- package/transpiled/tsconfig.tsbuildinfo +1 -1
- package/components/exposition.stash/operations/count.js +0 -7
|
@@ -4,10 +4,12 @@ export declare class Output implements Directive {
|
|
|
4
4
|
private readonly disabled;
|
|
5
5
|
private readonly omitted;
|
|
6
6
|
private readonly permissions;
|
|
7
|
+
private readonly allowed;
|
|
7
8
|
constructor(permissions: Permissions);
|
|
8
9
|
static validate(permissions: unknown): asserts permissions is Permissions;
|
|
9
10
|
preflight(context: Context): void;
|
|
10
11
|
private restriction;
|
|
12
|
+
/** Runs per entity of a collection, hence the set and the absence of intermediates. */
|
|
11
13
|
private fit;
|
|
12
14
|
}
|
|
13
15
|
export type Permissions = string[] | boolean;
|
|
@@ -31,6 +31,7 @@ class Output {
|
|
|
31
31
|
disabled = false;
|
|
32
32
|
omitted = true;
|
|
33
33
|
permissions = [];
|
|
34
|
+
allowed;
|
|
34
35
|
constructor(permissions) {
|
|
35
36
|
if (typeof permissions === 'boolean')
|
|
36
37
|
if (permissions)
|
|
@@ -39,6 +40,7 @@ class Output {
|
|
|
39
40
|
this.omitted = false;
|
|
40
41
|
else
|
|
41
42
|
this.permissions = permissions;
|
|
43
|
+
this.allowed = new Set(this.permissions);
|
|
42
44
|
}
|
|
43
45
|
static validate(permissions) {
|
|
44
46
|
schemas.output.validate(permissions, 'Incorrect \'io:output\' format');
|
|
@@ -66,10 +68,14 @@ class Output {
|
|
|
66
68
|
message.body = this.fit(message.body);
|
|
67
69
|
};
|
|
68
70
|
}
|
|
71
|
+
/** Runs per entity of a collection, hence the set and the absence of intermediates. */
|
|
69
72
|
fit(message) {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
const output = {};
|
|
74
|
+
// the keys of the entity, so that the response keeps the order it was built in
|
|
75
|
+
for (const key of Object.keys(message))
|
|
76
|
+
if (this.allowed.has(key))
|
|
77
|
+
output[key] = message[key];
|
|
78
|
+
return output;
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
exports.Output = Output;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Output.js","sourceRoot":"","sources":["../../../source/directives/io/Output.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAAoC;AACpC,uCAAkC;AAClC,mDAAoC;AAMpC,MAAa,MAAM;IACA,QAAQ,GAAY,KAAK,CAAA;IACzB,OAAO,GAAY,IAAI,CAAA;IACvB,WAAW,GAAa,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"Output.js","sourceRoot":"","sources":["../../../source/directives/io/Output.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAAoC;AACpC,uCAAkC;AAClC,mDAAoC;AAMpC,MAAa,MAAM;IACA,QAAQ,GAAY,KAAK,CAAA;IACzB,OAAO,GAAY,IAAI,CAAA;IACvB,WAAW,GAAa,EAAE,CAAA;IAC1B,OAAO,CAAa;IAErC,YAAoB,WAAwB;QAC1C,IAAI,OAAO,WAAW,KAAK,SAAS;YAClC,IAAI,WAAW;gBACb,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAA;;gBAEpB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;;YAGtB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAEhC,IAAI,CAAC,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC1C,CAAC;IAEM,MAAM,CAAC,QAAQ,CAAE,WAAoB;QAC1C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,gCAAgC,CAAC,CAAA;IACxE,CAAC;IAEM,SAAS,CAAE,OAAgB;QAChC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;IAC5D,CAAC;IAEO,WAAW,CAAE,OAAgB;QACnC,OAAO,CAAC,OAAwB,EAAQ,EAAE;YACxC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,CAAA;YACnE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,YAAY,oBAAM,CAAA;YAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CAAA;YAEhE,IAAI,IAAI,CAAC,QAAQ,IAAI,KAAK,IAAI,MAAM,IAAI,IAAI;gBAC1C,OAAM;YAER,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtE,IAAI,IAAI,CAAC,OAAO;oBACd,kBAAO,CAAC,IAAI,CAAC,4EAA4E,EACvF,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAA;gBAEnC,OAAO,OAAO,CAAC,IAAI,CAAA;gBAEnB,OAAM;YACR,CAAC;YAED,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAU,OAAO,CAAC,IAAI,EAC5C,oEAAoE,CAAC,CAAA;YAEvE,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;gBAC7B,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAiB,CAAC,CAAC,CAAA;;gBAExE,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;QACzC,CAAC,CAAA;IACH,CAAC;IAED,uFAAuF;IAC/E,GAAG,CAAE,OAAgB;QAC3B,MAAM,MAAM,GAAY,EAAE,CAAA;QAE1B,+EAA+E;QAC/E,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;YACpC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;gBACvB,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QAE9B,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAnED,wBAmEC"}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { type Declaration } from './lib/throttle';
|
|
2
|
-
import type { Remote } from '@toa.io/core';
|
|
1
|
+
import { type Declaration, type Sync } from './lib/throttle';
|
|
3
2
|
import type * as http from '../../HTTP';
|
|
4
3
|
import type { Parameter } from '../../RTD';
|
|
5
4
|
import type { Directive } from './Directive';
|
|
6
5
|
export declare class Throttle implements Directive {
|
|
7
6
|
private readonly quotas;
|
|
8
|
-
constructor(declaration: Declaration,
|
|
7
|
+
constructor(declaration: Declaration, sync: Sync, route: string);
|
|
9
8
|
static validate(declaration: unknown): asserts declaration is Declaration;
|
|
10
9
|
preflight(context: http.Context, parameters: Parameter[]): void;
|
|
11
|
-
settle(context: http.Context, output: http.OutgoingMessage):
|
|
12
|
-
dispose(): void;
|
|
10
|
+
settle(context: http.Context, output: http.OutgoingMessage): void;
|
|
13
11
|
}
|
|
@@ -29,22 +29,20 @@ const schemas = __importStar(require("./schemas"));
|
|
|
29
29
|
const throttle_1 = require("./lib/throttle");
|
|
30
30
|
class Throttle {
|
|
31
31
|
quotas;
|
|
32
|
-
constructor(declaration,
|
|
33
|
-
|
|
34
|
-
this.quotas
|
|
32
|
+
constructor(declaration, sync, route) {
|
|
33
|
+
this.quotas = throttle_1.Quotas.create((0, throttle_1.parse)(declaration), route);
|
|
34
|
+
sync.register(this.quotas);
|
|
35
35
|
}
|
|
36
36
|
static validate(declaration) {
|
|
37
37
|
schemas.throttle.validate(declaration, 'Incorrect \'io:throttle\' format');
|
|
38
38
|
}
|
|
39
39
|
preflight(context, parameters) {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const retry = this.quotas.check(context, parameters);
|
|
41
|
+
if (retry > 0)
|
|
42
|
+
throw new HTTP_1.TooManyRequests(retry);
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
dispose() {
|
|
47
|
-
this.quotas.dispose();
|
|
44
|
+
settle(context, output) {
|
|
45
|
+
this.quotas.use(context, output);
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
exports.Throttle = Throttle;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Throttle.js","sourceRoot":"","sources":["../../../source/directives/io/Throttle.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAA4C;AAC5C,mDAAoC;AACpC,
|
|
1
|
+
{"version":3,"file":"Throttle.js","sourceRoot":"","sources":["../../../source/directives/io/Throttle.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qCAA4C;AAC5C,mDAAoC;AACpC,6CAA2E;AAK3E,MAAa,QAAQ;IACF,MAAM,CAAQ;IAE/B,YAAoB,WAAwB,EAAE,IAAU,EAAE,KAAa;QACrE,IAAI,CAAC,MAAM,GAAG,iBAAM,CAAC,MAAM,CAAC,IAAA,gBAAK,EAAC,WAAW,CAAC,EAAE,KAAK,CAAC,CAAA;QAEtD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC;IAEM,MAAM,CAAC,QAAQ,CAAE,WAAoB;QAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,kCAAkC,CAAC,CAAA;IAC5E,CAAC;IAEM,SAAS,CAAE,OAAqB,EAAE,UAAuB;QAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;QAEpD,IAAI,KAAK,GAAG,CAAC;YACX,MAAM,IAAI,sBAAe,CAAC,KAAK,CAAC,CAAA;IACpC,CAAC;IAEM,MAAM,CAAE,OAAqB,EAAE,MAA4B;QAChE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAClC,CAAC;CACF;AAvBD,4BAuBC"}
|
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parse = void 0;
|
|
4
4
|
function parse(declaration) {
|
|
5
|
-
const { key, condition, requests, interval
|
|
5
|
+
const { key, condition, requests, interval } = declaration;
|
|
6
6
|
return {
|
|
7
7
|
key: mapKey(key),
|
|
8
8
|
condition: mapCondition(condition),
|
|
9
9
|
requests,
|
|
10
|
-
interval: interval * 1000
|
|
11
|
-
cooldown: cooldown * 1000
|
|
10
|
+
interval: interval * 1000
|
|
12
11
|
};
|
|
13
12
|
}
|
|
14
13
|
exports.parse = parse;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Configuration.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/Configuration.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"Configuration.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/Configuration.ts"],"names":[],"mappings":";;;AA+BA,SAAgB,KAAK,CAAE,WAAwB;IAC7C,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAA;IAE1D,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC;QAChB,SAAS,EAAE,YAAY,CAAC,SAAS,CAAC;QAClC,QAAQ;QACR,QAAQ,EAAE,QAAQ,GAAG,IAAI;KAC1B,CAAA;AACH,CAAC;AATD,sBASC;AAED,SAAS,MAAM,CAAE,WAA2B;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IAExE,qEAAqE;IACrE,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAkB,EAAE,CAC/C,OAAO,KAAK,KAAK,QAAQ;QACvB,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACrB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAChD,CAAC,EAAE,MAAM,EAAE,MAA4B,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;AAC7D,CAAC;AAED,SAAS,YAAY,CAAE,WAAkC;IACvD,IAAI,WAAW,KAAK,SAAS;QAC3B,OAAM;IAER,uDAAuD;IACvD,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IAC3E,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;IAEpF,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAmB,CAAA;AACnG,CAAC"}
|
|
@@ -1,45 +1,69 @@
|
|
|
1
1
|
import { Keys } from './Keys';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Batch } from './Sync';
|
|
3
3
|
import type { Configuration } from './Configuration';
|
|
4
4
|
import type { Parameter } from '../../../../RTD';
|
|
5
5
|
import type { Input as Context, Output } from '../../../../io';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* A quota per key, metered by GCRA, decided in this process and shared with the others.
|
|
8
8
|
*
|
|
9
|
-
* The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The state of a key is one theoretical arrival time: the moment the key would be back
|
|
10
|
+
* at zero if nothing more arrived. An admitted request pushes it `emission` further out,
|
|
11
|
+
* time drags it back, and a request is admitted while it stays within `capacity` of now.
|
|
12
|
+
* So `requests` is what a key may spend at once, and `requests / interval` what it earns
|
|
13
|
+
* back — no window to burst across the edge of, and no lockout to time out of.
|
|
14
|
+
*
|
|
15
|
+
* Deciding needs nothing but the local map, which is what keeps the request path free of
|
|
16
|
+
* I/O. What the other gateways have spent arrives on {@link Sync}'s tick, as a debt in
|
|
17
|
+
* milliseconds — a duration, so it carries across processes without their clocks having
|
|
18
|
+
* to agree, and additive, which is what lets each process report only its own increments.
|
|
13
19
|
*/
|
|
14
20
|
export declare class Quotas {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
/** How often the debt is worth reconciling with the other gateways, in milliseconds. */
|
|
22
|
+
readonly period: number;
|
|
23
|
+
/** Milliseconds of debt an admitted request adds, `interval / requests`. */
|
|
24
|
+
private readonly emission;
|
|
25
|
+
/** Debt a key may carry before it is refused, `interval` — a burst of `requests`. */
|
|
26
|
+
private readonly capacity;
|
|
18
27
|
private readonly keys;
|
|
19
|
-
private readonly
|
|
20
|
-
private readonly
|
|
21
|
-
private readonly
|
|
28
|
+
private readonly prefix;
|
|
29
|
+
private readonly conditional;
|
|
30
|
+
private readonly entries;
|
|
22
31
|
/**
|
|
23
|
-
* The key computed at preflight, for settle to
|
|
32
|
+
* The key computed at preflight, for settle to charge against.
|
|
24
33
|
*
|
|
25
34
|
* `segment` reads the route parameters, which settle is not given, and `identity`
|
|
26
35
|
* can be refreshed between the two — so recomputing there would key a request
|
|
27
|
-
* differently than it was checked, and
|
|
36
|
+
* differently than it was checked, and throttling would quietly stop working.
|
|
28
37
|
*/
|
|
29
38
|
private readonly keyed;
|
|
30
|
-
private remote;
|
|
31
39
|
constructor(options: Options);
|
|
32
|
-
static create(configuration: Configuration,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
static create(configuration: Configuration, route?: string): Quotas;
|
|
41
|
+
/**
|
|
42
|
+
* Seconds to wait before the request would be admitted, or zero when it is.
|
|
43
|
+
*
|
|
44
|
+
* Charges here when nothing conditions the count, so that the request is metered by
|
|
45
|
+
* the same call that admits it; otherwise only the response can tell whether it counts,
|
|
46
|
+
* and {@link use} charges once it can.
|
|
47
|
+
*/
|
|
48
|
+
check(context: Context, parameters: Parameter[]): number;
|
|
49
|
+
/** Charges what the condition accepts, once the response can be matched against it. */
|
|
50
|
+
use(input: Context, output: Output): void;
|
|
51
|
+
/**
|
|
52
|
+
* Adds what is worth reporting to the batch, and drops what has gone quiet.
|
|
53
|
+
*
|
|
54
|
+
* The debt is not cleared here but in {@link settled}, so a tick that fails to reach
|
|
55
|
+
* Redis leaves it to be reported by the next one instead of losing it.
|
|
56
|
+
*/
|
|
57
|
+
flush(now: number, batch: Batch[]): void;
|
|
58
|
+
/** Takes the group's debt back, and clears what this process contributed to it. */
|
|
59
|
+
settled(reported: Batch, debt: number, now: number): void;
|
|
60
|
+
name(key: string): string;
|
|
61
|
+
private charge;
|
|
37
62
|
}
|
|
38
63
|
interface Options {
|
|
39
64
|
keys: Keys;
|
|
40
65
|
requests: number;
|
|
41
66
|
interval: number;
|
|
42
|
-
|
|
43
|
-
counter: Promise<Remote>;
|
|
67
|
+
conditional: boolean;
|
|
44
68
|
}
|
|
45
69
|
export {};
|
|
@@ -1,77 +1,138 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Quotas = void 0;
|
|
4
|
-
const openspan_1 = require("openspan");
|
|
5
4
|
const Keys_1 = require("./Keys");
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
6
|
+
* A quota per key, metered by GCRA, decided in this process and shared with the others.
|
|
8
7
|
*
|
|
9
|
-
* The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* The state of a key is one theoretical arrival time: the moment the key would be back
|
|
9
|
+
* at zero if nothing more arrived. An admitted request pushes it `emission` further out,
|
|
10
|
+
* time drags it back, and a request is admitted while it stays within `capacity` of now.
|
|
11
|
+
* So `requests` is what a key may spend at once, and `requests / interval` what it earns
|
|
12
|
+
* back — no window to burst across the edge of, and no lockout to time out of.
|
|
13
|
+
*
|
|
14
|
+
* Deciding needs nothing but the local map, which is what keeps the request path free of
|
|
15
|
+
* I/O. What the other gateways have spent arrives on {@link Sync}'s tick, as a debt in
|
|
16
|
+
* milliseconds — a duration, so it carries across processes without their clocks having
|
|
17
|
+
* to agree, and additive, which is what lets each process report only its own increments.
|
|
13
18
|
*/
|
|
14
19
|
class Quotas {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
/** How often the debt is worth reconciling with the other gateways, in milliseconds. */
|
|
21
|
+
period;
|
|
22
|
+
/** Milliseconds of debt an admitted request adds, `interval / requests`. */
|
|
23
|
+
emission;
|
|
24
|
+
/** Debt a key may carry before it is refused, `interval` — a burst of `requests`. */
|
|
25
|
+
capacity;
|
|
18
26
|
keys;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
prefix;
|
|
28
|
+
conditional;
|
|
29
|
+
entries = new Map();
|
|
22
30
|
/**
|
|
23
|
-
* The key computed at preflight, for settle to
|
|
31
|
+
* The key computed at preflight, for settle to charge against.
|
|
24
32
|
*
|
|
25
33
|
* `segment` reads the route parameters, which settle is not given, and `identity`
|
|
26
34
|
* can be refreshed between the two — so recomputing there would key a request
|
|
27
|
-
* differently than it was checked, and
|
|
35
|
+
* differently than it was checked, and throttling would quietly stop working.
|
|
28
36
|
*/
|
|
29
37
|
keyed = new WeakMap();
|
|
30
|
-
remote = null;
|
|
31
38
|
constructor(options) {
|
|
32
|
-
this.requests = options.requests;
|
|
33
|
-
this.interval = options.interval;
|
|
34
|
-
this.cooldown = options.cooldown;
|
|
35
39
|
this.keys = options.keys;
|
|
36
|
-
this.
|
|
40
|
+
this.conditional = options.conditional;
|
|
41
|
+
this.emission = options.interval / options.requests;
|
|
42
|
+
this.capacity = options.interval;
|
|
43
|
+
this.period = period(options.interval);
|
|
44
|
+
// a key means one budget, so quotas that do not share one must not share a key
|
|
45
|
+
this.prefix = `t:${options.requests}:${options.interval}:`;
|
|
37
46
|
}
|
|
38
|
-
static create(configuration,
|
|
39
|
-
const { requests, interval,
|
|
40
|
-
const keys = Keys_1.Keys.create(configuration.key,
|
|
41
|
-
return new this({ requests, interval,
|
|
47
|
+
static create(configuration, route = '') {
|
|
48
|
+
const { requests, interval, condition } = configuration;
|
|
49
|
+
const keys = Keys_1.Keys.create(configuration.key, condition, route);
|
|
50
|
+
return new this({ keys, requests, interval, conditional: condition !== undefined });
|
|
42
51
|
}
|
|
43
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Seconds to wait before the request would be admitted, or zero when it is.
|
|
54
|
+
*
|
|
55
|
+
* Charges here when nothing conditions the count, so that the request is metered by
|
|
56
|
+
* the same call that admits it; otherwise only the response can tell whether it counts,
|
|
57
|
+
* and {@link use} charges once it can.
|
|
58
|
+
*/
|
|
59
|
+
check(context, parameters) {
|
|
44
60
|
const key = this.keys.get(context, parameters);
|
|
45
61
|
this.keyed.set(context, key);
|
|
46
|
-
|
|
62
|
+
const now = Date.now();
|
|
63
|
+
const entry = this.entries.get(key);
|
|
64
|
+
const tat = Math.max(entry?.tat ?? now, now);
|
|
65
|
+
const admit = tat + this.emission - this.capacity;
|
|
66
|
+
if (admit > now)
|
|
67
|
+
return Math.ceil((admit - now) / 1000);
|
|
68
|
+
if (!this.conditional)
|
|
69
|
+
this.charge(key, entry, tat);
|
|
70
|
+
return 0;
|
|
47
71
|
}
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
/** Charges what the condition accepts, once the response can be matched against it. */
|
|
73
|
+
use(input, output) {
|
|
74
|
+
if (!this.conditional || !this.keys.matches(input, output))
|
|
50
75
|
return;
|
|
51
|
-
// preflight always runs first, and a request it
|
|
76
|
+
// preflight always runs first, and a request it refuses never reaches settle
|
|
52
77
|
const key = this.keyed.get(input) ?? this.keys.get(input);
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
78
|
+
const now = Date.now();
|
|
79
|
+
const entry = this.entries.get(key);
|
|
80
|
+
const tat = Math.max(entry?.tat ?? now, now);
|
|
81
|
+
this.charge(key, entry, tat);
|
|
57
82
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Adds what is worth reporting to the batch, and drops what has gone quiet.
|
|
85
|
+
*
|
|
86
|
+
* The debt is not cleared here but in {@link settled}, so a tick that fails to reach
|
|
87
|
+
* Redis leaves it to be reported by the next one instead of losing it.
|
|
88
|
+
*/
|
|
89
|
+
flush(now, batch) {
|
|
90
|
+
for (const [key, entry] of this.entries) {
|
|
91
|
+
const delta = Math.round(entry.debt);
|
|
92
|
+
if (delta > 0) {
|
|
93
|
+
batch.push({ quotas: this, key, delta });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// out of debt and with nothing left to report, a key says no more than an absent one
|
|
97
|
+
if (entry.tat <= now)
|
|
98
|
+
this.entries.delete(key);
|
|
99
|
+
}
|
|
62
100
|
}
|
|
63
|
-
|
|
64
|
-
|
|
101
|
+
/** Takes the group's debt back, and clears what this process contributed to it. */
|
|
102
|
+
settled(reported, debt, now) {
|
|
103
|
+
const entry = this.entries.get(reported.key);
|
|
104
|
+
if (entry === undefined) {
|
|
105
|
+
if (debt > 0)
|
|
106
|
+
this.entries.set(reported.key, { tat: now + debt, debt: 0 });
|
|
65
107
|
return;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
|
|
108
|
+
}
|
|
109
|
+
entry.debt -= reported.delta;
|
|
110
|
+
// the group has spent at least what this process has, so its debt is the one to keep
|
|
111
|
+
entry.tat = Math.max(entry.tat, now + debt);
|
|
112
|
+
}
|
|
113
|
+
name(key) {
|
|
114
|
+
return this.prefix + key;
|
|
115
|
+
}
|
|
116
|
+
charge(key, entry, tat) {
|
|
117
|
+
if (entry === undefined)
|
|
118
|
+
this.entries.set(key, { tat: tat + this.emission, debt: this.emission });
|
|
119
|
+
else {
|
|
120
|
+
entry.tat = tat + this.emission;
|
|
121
|
+
entry.debt += this.emission;
|
|
122
|
+
}
|
|
74
123
|
}
|
|
75
124
|
}
|
|
76
125
|
exports.Quotas = Quotas;
|
|
126
|
+
/**
|
|
127
|
+
* How often to reconcile, in milliseconds.
|
|
128
|
+
*
|
|
129
|
+
* Reconciling is what bounds the overshoot — between two ticks a gateway is going on
|
|
130
|
+
* what it alone has spent — so it is worth doing an order of magnitude more often than
|
|
131
|
+
* the interval, within reason on either end.
|
|
132
|
+
*/
|
|
133
|
+
function period(interval) {
|
|
134
|
+
return Math.min(Math.max(interval / 10, MIN_PERIOD), MAX_PERIOD);
|
|
135
|
+
}
|
|
136
|
+
const MIN_PERIOD = 250;
|
|
137
|
+
const MAX_PERIOD = 2000;
|
|
77
138
|
//# sourceMappingURL=Quotas.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Quotas.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/Quotas.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"Quotas.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/Quotas.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Quotas } from './Quotas';
|
|
2
|
+
import type { Remote } from '@toa.io/core';
|
|
3
|
+
/**
|
|
4
|
+
* Reconciles every quota of the process with the other gateways, on one tick.
|
|
5
|
+
*
|
|
6
|
+
* Nothing here is on the request path. Each tick takes what the quotas have spent since
|
|
7
|
+
* the last one and sends all of it as a single call, so the cost of staying in step is
|
|
8
|
+
* one round trip a tick — not one per key, and not one per directive. What comes back is
|
|
9
|
+
* the debt the whole group has reached, which the quotas then decide on locally.
|
|
10
|
+
*/
|
|
11
|
+
export declare class Sync {
|
|
12
|
+
private readonly stash;
|
|
13
|
+
private readonly quotas;
|
|
14
|
+
private remote;
|
|
15
|
+
private timer;
|
|
16
|
+
private period;
|
|
17
|
+
/** Ticks do not overlap: a slow round trip delays reconciling, it does not double it. */
|
|
18
|
+
private reconciling;
|
|
19
|
+
constructor(stash: Promise<Remote>);
|
|
20
|
+
register(quotas: Quotas): void;
|
|
21
|
+
dispose(): void;
|
|
22
|
+
private start;
|
|
23
|
+
private readonly tick;
|
|
24
|
+
private reconcile;
|
|
25
|
+
}
|
|
26
|
+
export interface Batch {
|
|
27
|
+
quotas: Quotas;
|
|
28
|
+
key: string;
|
|
29
|
+
delta: number;
|
|
30
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Sync = void 0;
|
|
4
|
+
const openspan_1 = require("openspan");
|
|
5
|
+
/**
|
|
6
|
+
* Reconciles every quota of the process with the other gateways, on one tick.
|
|
7
|
+
*
|
|
8
|
+
* Nothing here is on the request path. Each tick takes what the quotas have spent since
|
|
9
|
+
* the last one and sends all of it as a single call, so the cost of staying in step is
|
|
10
|
+
* one round trip a tick — not one per key, and not one per directive. What comes back is
|
|
11
|
+
* the debt the whole group has reached, which the quotas then decide on locally.
|
|
12
|
+
*/
|
|
13
|
+
class Sync {
|
|
14
|
+
stash;
|
|
15
|
+
quotas = [];
|
|
16
|
+
remote = null;
|
|
17
|
+
timer = null;
|
|
18
|
+
period = Infinity;
|
|
19
|
+
/** Ticks do not overlap: a slow round trip delays reconciling, it does not double it. */
|
|
20
|
+
reconciling = false;
|
|
21
|
+
constructor(stash) {
|
|
22
|
+
this.stash = stash;
|
|
23
|
+
}
|
|
24
|
+
register(quotas) {
|
|
25
|
+
this.quotas.push(quotas);
|
|
26
|
+
// the shortest interval sets the pace, and reconciling the others more often than
|
|
27
|
+
// they asked for only makes them more accurate — an idle quota contributes nothing
|
|
28
|
+
if (quotas.period < this.period) {
|
|
29
|
+
this.period = quotas.period;
|
|
30
|
+
this.start();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
dispose() {
|
|
34
|
+
if (this.timer !== null) {
|
|
35
|
+
clearInterval(this.timer);
|
|
36
|
+
this.timer = null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
start() {
|
|
40
|
+
this.dispose();
|
|
41
|
+
this.timer = setInterval(this.tick, this.period);
|
|
42
|
+
this.timer.unref();
|
|
43
|
+
}
|
|
44
|
+
tick = () => {
|
|
45
|
+
if (this.reconciling)
|
|
46
|
+
return;
|
|
47
|
+
const now = Date.now();
|
|
48
|
+
const batch = [];
|
|
49
|
+
for (const quotas of this.quotas)
|
|
50
|
+
quotas.flush(now, batch);
|
|
51
|
+
if (batch.length === 0)
|
|
52
|
+
return;
|
|
53
|
+
this.reconciling = true;
|
|
54
|
+
void this.reconcile(batch).finally(() => {
|
|
55
|
+
this.reconciling = false;
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
async reconcile(batch) {
|
|
59
|
+
try {
|
|
60
|
+
this.remote ??= await this.stash;
|
|
61
|
+
const keys = batch.map((entry) => entry.quotas.name(entry.key));
|
|
62
|
+
const deltas = batch.map((entry) => entry.delta);
|
|
63
|
+
const debts = await this.remote.invoke('meter', { input: { keys, deltas } });
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
for (let i = 0; i < batch.length; i++)
|
|
66
|
+
batch[i].quotas.settled(batch[i], debts[i], now);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// what was not reported stays to be reported by the next tick, and until then
|
|
70
|
+
// this gateway keeps throttling on what it alone has seen, and keeps serving
|
|
71
|
+
openspan_1.console.warn('Throttling could not reconcile with the group', error);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.Sync = Sync;
|
|
76
|
+
//# sourceMappingURL=Sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sync.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/Sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parse = exports.Quotas = void 0;
|
|
3
|
+
exports.parse = exports.Sync = exports.Quotas = void 0;
|
|
4
4
|
var Quotas_1 = require("./Quotas");
|
|
5
5
|
Object.defineProperty(exports, "Quotas", { enumerable: true, get: function () { return Quotas_1.Quotas; } });
|
|
6
|
+
var Sync_1 = require("./Sync");
|
|
7
|
+
Object.defineProperty(exports, "Sync", { enumerable: true, get: function () { return Sync_1.Sync; } });
|
|
6
8
|
var Configuration_1 = require("./Configuration");
|
|
7
9
|
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return Configuration_1.parse; } });
|
|
8
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/index.ts"],"names":[],"mappings":";;;AAAA,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,iDAA6E;AAApE,sGAAA,KAAK,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../source/directives/io/lib/throttle/index.ts"],"names":[],"mappings":";;;AAAA,mCAAiC;AAAxB,gGAAA,MAAM,OAAA;AACf,+BAA6B;AAApB,4FAAA,IAAI,OAAA;AACb,iDAA6E;AAApE,sGAAA,KAAK,OAAA"}
|