@jr200-labs/xstate-nats 0.9.13 → 0.9.14
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/dist/actions/kv.d.ts +1 -0
- package/dist/actions/kv.d.ts.map +1 -1
- package/dist/actions/kv.js +9 -2
- package/dist/actions/kv.js.map +1 -1
- package/dist/actions/subject.d.ts +2 -0
- package/dist/actions/subject.d.ts.map +1 -1
- package/dist/actions/subject.js +8 -12
- package/dist/actions/subject.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/machines/kv.d.ts +17 -3
- package/dist/machines/kv.d.ts.map +1 -1
- package/dist/machines/kv.js +46 -10
- package/dist/machines/kv.js.map +1 -1
- package/dist/machines/root.d.ts +51 -5
- package/dist/machines/root.d.ts.map +1 -1
- package/dist/machines/root.js +9 -0
- package/dist/machines/root.js.map +1 -1
- package/dist/machines/subject.d.ts +17 -3
- package/dist/machines/subject.d.ts.map +1 -1
- package/dist/machines/subject.js +56 -10
- package/dist/machines/subject.js.map +1 -1
- package/dist/traffic.d.ts +33 -0
- package/dist/traffic.d.ts.map +1 -0
- package/dist/traffic.js +99 -0
- package/dist/traffic.js.map +1 -0
- package/package.json +1 -1
- package/src/actions/kv.test.ts +59 -0
- package/src/actions/kv.ts +10 -2
- package/src/actions/subject.test.ts +41 -0
- package/src/actions/subject.ts +10 -14
- package/src/index.ts +14 -0
- package/src/machines/kv.ts +63 -11
- package/src/machines/root.ts +11 -0
- package/src/machines/subject.test.ts +86 -1
- package/src/machines/subject.ts +74 -11
- package/src/traffic.test.ts +62 -0
- package/src/traffic.ts +150 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Subscription, PublishOptions, NatsConnection, RequestOptions } from '@nats-io/nats-core';
|
|
2
2
|
import { RequestResult, SubjectSubscriptionConfig } from '../actions/subject';
|
|
3
|
+
import { NatsTrafficMetrics, NatsTrafficRecordEvent, NatsTrafficResetEvent } from '../traffic';
|
|
3
4
|
type InternalEvents = {
|
|
4
5
|
type: 'ERROR';
|
|
5
6
|
error: Error;
|
|
6
|
-
};
|
|
7
|
+
} | NatsTrafficRecordEvent;
|
|
7
8
|
export type ExternalEvents = {
|
|
8
9
|
type: 'SUBJECT.CONNECT';
|
|
9
10
|
connection: NatsConnection;
|
|
@@ -37,7 +38,7 @@ export type ExternalEvents = {
|
|
|
37
38
|
subject: string;
|
|
38
39
|
} | {
|
|
39
40
|
type: 'SUBJECT.UNSUBSCRIBE_ALL';
|
|
40
|
-
};
|
|
41
|
+
} | NatsTrafficResetEvent;
|
|
41
42
|
export type Events = InternalEvents | ExternalEvents;
|
|
42
43
|
export interface Context {
|
|
43
44
|
uid: string;
|
|
@@ -45,9 +46,22 @@ export interface Context {
|
|
|
45
46
|
subscriptions: Map<string, Subscription>;
|
|
46
47
|
subscriptionConfigs: Map<string, SubjectSubscriptionConfig>;
|
|
47
48
|
syncRequired: number;
|
|
49
|
+
trafficMetrics: NatsTrafficMetrics;
|
|
48
50
|
error?: Error;
|
|
49
51
|
}
|
|
50
|
-
export declare const subjectManagerLogic: import("xstate").StateMachine<Context,
|
|
52
|
+
export declare const subjectManagerLogic: import("xstate").StateMachine<Context, NatsTrafficRecordEvent | {
|
|
53
|
+
type: "METRICS.RESET_UPLOAD";
|
|
54
|
+
source?: import("..").NatsTrafficMetricSource;
|
|
55
|
+
} | {
|
|
56
|
+
type: "METRICS.RESET_DOWNLOAD";
|
|
57
|
+
source?: import("..").NatsTrafficMetricSource;
|
|
58
|
+
} | {
|
|
59
|
+
type: "METRICS.RESET_ALL";
|
|
60
|
+
source?: import("..").NatsTrafficMetricSource;
|
|
61
|
+
} | {
|
|
62
|
+
type: "ERROR";
|
|
63
|
+
error: Error;
|
|
64
|
+
} | {
|
|
51
65
|
type: "SUBJECT.CONNECT";
|
|
52
66
|
connection: NatsConnection;
|
|
53
67
|
} | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subject.d.ts","sourceRoot":"","sources":["../../src/machines/subject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEjG,OAAO,EACL,aAAa,EACb,yBAAyB,EAI1B,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"subject.d.ts","sourceRoot":"","sources":["../../src/machines/subject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAEjG,OAAO,EACL,aAAa,EACb,yBAAyB,EAI1B,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAGL,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EAKtB,MAAM,YAAY,CAAA;AAGnB,KAAK,cAAc,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,sBAAsB,CAAA;AAG9E,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,UAAU,EAAE,cAAc,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,sBAAsB,CAAA;CAAE,GAChC;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,GAAG,CAAA;IACZ,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI,CAAA;CAC/E,GACD;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,GAAG,CAAA;IACZ,IAAI,CAAC,EAAE,cAAc,CAAA;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7B,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAA;CAClD,GACD;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,yBAAyB,CAAA;CAAE,GACnC,qBAAqB,CAAA;AAEzB,MAAM,MAAM,MAAM,GAAG,cAAc,GAAG,cAAc,CAAA;AAEpD,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAA;IACX,gBAAgB,EAAE,cAAc,GAAG,IAAI,CAAA;IACvC,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACxC,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAA;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,kBAAkB,CAAA;IAClC,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;UAvCF,OAAO;WAAS,KAAK;;UAIvC,iBAAiB;gBAAc,cAAc;;UAC7C,mBAAmB;;UACnB,sBAAsB;;UAEtB,iBAAiB;aACd,MAAM;aACN,GAAG;WACL,cAAc;sBACH,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,KAAK,CAAA;KAAE,KAAK,IAAI;;UAGxE,iBAAiB;aACd,MAAM;aACN,GAAG;WACL,cAAc;cACX,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI;sBACX,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI;;UAE3C,mBAAmB;YAAU,yBAAyB;;UACtD,qBAAqB;aAAW,MAAM;;UACtC,yBAAyB;;;;;;;;;;;;;EAoQnC,CAAA"}
|
package/dist/machines/subject.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assign, sendParent, setup } from 'xstate';
|
|
2
2
|
import { subjectConsolidateState, subjectRequest, subjectPublish, } from '../actions/subject';
|
|
3
|
+
import { byteLength, createEmptyTrafficMetrics, recordTrafficMetric, resetTrafficAll, resetTrafficDownload, resetTrafficUpload, } from '../traffic';
|
|
3
4
|
export const subjectManagerLogic = setup({
|
|
4
5
|
types: {
|
|
5
6
|
context: {},
|
|
@@ -19,19 +20,49 @@ export const subjectManagerLogic = setup({
|
|
|
19
20
|
subscriptionConfigs: new Map(),
|
|
20
21
|
cachedConnection: null,
|
|
21
22
|
syncRequired: 0,
|
|
23
|
+
trafficMetrics: createEmptyTrafficMetrics(),
|
|
22
24
|
error: undefined,
|
|
23
25
|
},
|
|
24
26
|
on: {
|
|
27
|
+
'METRICS.RECORD': {
|
|
28
|
+
actions: assign({
|
|
29
|
+
trafficMetrics: ({ context, event }) => recordTrafficMetric(context.trafficMetrics, event),
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
'METRICS.RESET_UPLOAD': {
|
|
33
|
+
actions: assign({
|
|
34
|
+
trafficMetrics: ({ context, event }) => resetTrafficUpload(context.trafficMetrics, event.source),
|
|
35
|
+
}),
|
|
36
|
+
},
|
|
37
|
+
'METRICS.RESET_DOWNLOAD': {
|
|
38
|
+
actions: assign({
|
|
39
|
+
trafficMetrics: ({ context, event }) => resetTrafficDownload(context.trafficMetrics, event.source),
|
|
40
|
+
}),
|
|
41
|
+
},
|
|
42
|
+
'METRICS.RESET_ALL': {
|
|
43
|
+
actions: assign({
|
|
44
|
+
trafficMetrics: ({ context, event }) => resetTrafficAll(context.trafficMetrics, event.source),
|
|
45
|
+
}),
|
|
46
|
+
},
|
|
25
47
|
'SUBJECT.SUBSCRIBE': {
|
|
26
48
|
actions: [
|
|
27
|
-
assign({
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
assign(({ context, event, self }) => {
|
|
50
|
+
if (event.type !== 'SUBJECT.SUBSCRIBE')
|
|
51
|
+
return {};
|
|
52
|
+
const config = {
|
|
53
|
+
...event.config,
|
|
54
|
+
onDownloadBytes: (bytes) => self.send({
|
|
55
|
+
type: 'METRICS.RECORD',
|
|
56
|
+
source: 'nats.subscribe',
|
|
57
|
+
downloadBytes: bytes,
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
const newConfigs = new Map(context.subscriptionConfigs);
|
|
61
|
+
newConfigs.set(config.subject, config);
|
|
62
|
+
return {
|
|
63
|
+
subscriptionConfigs: newConfigs,
|
|
64
|
+
syncRequired: context.syncRequired + 1,
|
|
65
|
+
};
|
|
35
66
|
}),
|
|
36
67
|
],
|
|
37
68
|
},
|
|
@@ -103,7 +134,12 @@ export const subjectManagerLogic = setup({
|
|
|
103
134
|
target: 'subject_disconnecting',
|
|
104
135
|
},
|
|
105
136
|
'SUBJECT.REQUEST': {
|
|
106
|
-
actions: assign(({ event, context }) => {
|
|
137
|
+
actions: assign(({ event, context, self }) => {
|
|
138
|
+
self.send({
|
|
139
|
+
type: 'METRICS.RECORD',
|
|
140
|
+
source: 'nats.request',
|
|
141
|
+
uploadBytes: byteLength(event.payload),
|
|
142
|
+
});
|
|
107
143
|
subjectRequest({
|
|
108
144
|
input: {
|
|
109
145
|
connection: context.cachedConnection,
|
|
@@ -112,6 +148,11 @@ export const subjectManagerLogic = setup({
|
|
|
112
148
|
opts: event.opts,
|
|
113
149
|
callback: event.callback,
|
|
114
150
|
onRequestResult: event.onRequestResult,
|
|
151
|
+
onDownloadBytes: (bytes) => self.send({
|
|
152
|
+
type: 'METRICS.RECORD',
|
|
153
|
+
source: 'nats.request',
|
|
154
|
+
downloadBytes: bytes,
|
|
155
|
+
}),
|
|
115
156
|
},
|
|
116
157
|
});
|
|
117
158
|
return {};
|
|
@@ -119,7 +160,12 @@ export const subjectManagerLogic = setup({
|
|
|
119
160
|
},
|
|
120
161
|
'SUBJECT.PUBLISH': {
|
|
121
162
|
actions: [
|
|
122
|
-
({ context, event }) => {
|
|
163
|
+
({ context, event, self }) => {
|
|
164
|
+
self.send({
|
|
165
|
+
type: 'METRICS.RECORD',
|
|
166
|
+
source: 'nats.publish',
|
|
167
|
+
uploadBytes: byteLength(event.payload),
|
|
168
|
+
});
|
|
123
169
|
subjectPublish({
|
|
124
170
|
input: {
|
|
125
171
|
connection: context.cachedConnection,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"subject.js","sourceRoot":"","sources":["../../src/machines/subject.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAClD,OAAO,EAGL,uBAAuB,EACvB,cAAc,EACd,cAAc,GACf,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"subject.js","sourceRoot":"","sources":["../../src/machines/subject.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type NatsTrafficMetricSource = 'nats.publish' | 'nats.request' | 'nats.subscribe' | 'nats.kv';
|
|
2
|
+
export type NatsTrafficDirection = 'upload' | 'download';
|
|
3
|
+
export interface NatsTrafficMetricBucket {
|
|
4
|
+
uploadBytes: number;
|
|
5
|
+
downloadBytes: number;
|
|
6
|
+
}
|
|
7
|
+
export interface NatsTrafficMetrics extends NatsTrafficMetricBucket {
|
|
8
|
+
bySource: Partial<Record<NatsTrafficMetricSource, NatsTrafficMetricBucket>>;
|
|
9
|
+
}
|
|
10
|
+
export type NatsTrafficRecordEvent = {
|
|
11
|
+
type: 'METRICS.RECORD';
|
|
12
|
+
source: NatsTrafficMetricSource;
|
|
13
|
+
uploadBytes?: number;
|
|
14
|
+
downloadBytes?: number;
|
|
15
|
+
};
|
|
16
|
+
export type NatsTrafficResetEvent = {
|
|
17
|
+
type: 'METRICS.RESET_UPLOAD';
|
|
18
|
+
source?: NatsTrafficMetricSource;
|
|
19
|
+
} | {
|
|
20
|
+
type: 'METRICS.RESET_DOWNLOAD';
|
|
21
|
+
source?: NatsTrafficMetricSource;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'METRICS.RESET_ALL';
|
|
24
|
+
source?: NatsTrafficMetricSource;
|
|
25
|
+
};
|
|
26
|
+
export type NatsTrafficEvent = NatsTrafficRecordEvent | NatsTrafficResetEvent;
|
|
27
|
+
export declare function createEmptyTrafficMetrics(): NatsTrafficMetrics;
|
|
28
|
+
export declare function byteLength(value: unknown): number;
|
|
29
|
+
export declare function recordTrafficMetric(metrics: NatsTrafficMetrics, event: NatsTrafficRecordEvent): NatsTrafficMetrics;
|
|
30
|
+
export declare function resetTrafficUpload(metrics: NatsTrafficMetrics, source?: NatsTrafficMetricSource): NatsTrafficMetrics;
|
|
31
|
+
export declare function resetTrafficDownload(metrics: NatsTrafficMetrics, source?: NatsTrafficMetricSource): NatsTrafficMetrics;
|
|
32
|
+
export declare function resetTrafficAll(metrics: NatsTrafficMetrics, source?: NatsTrafficMetricSource): NatsTrafficMetrics;
|
|
33
|
+
//# sourceMappingURL=traffic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traffic.d.ts","sourceRoot":"","sources":["../src/traffic.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,uBAAuB,GAAG,cAAc,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAA;AAEpG,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,UAAU,CAAA;AAExD,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IACjE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,uBAAuB,EAAE,uBAAuB,CAAC,CAAC,CAAA;CAC5E;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,gBAAgB,CAAA;IACtB,MAAM,EAAE,uBAAuB,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,MAAM,CAAC,EAAE,uBAAuB,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,wBAAwB,CAAC;IAAC,MAAM,CAAC,EAAE,uBAAuB,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,MAAM,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAA;AAEnE,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,GAAG,qBAAqB,CAAA;AAI7E,wBAAgB,yBAAyB,IAAI,kBAAkB,CAM9D;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAYjD;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,sBAAsB,GAC5B,kBAAkB,CAkBpB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,uBAAuB,GAC/B,kBAAkB,CAEpB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,uBAAuB,GAC/B,kBAAkB,CAEpB;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,kBAAkB,EAC3B,MAAM,CAAC,EAAE,uBAAuB,GAC/B,kBAAkB,CAYpB"}
|
package/dist/traffic.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const textEncoder = new TextEncoder();
|
|
2
|
+
export function createEmptyTrafficMetrics() {
|
|
3
|
+
return {
|
|
4
|
+
uploadBytes: 0,
|
|
5
|
+
downloadBytes: 0,
|
|
6
|
+
bySource: {},
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function byteLength(value) {
|
|
10
|
+
if (value == null)
|
|
11
|
+
return 0;
|
|
12
|
+
if (value instanceof Uint8Array)
|
|
13
|
+
return value.byteLength;
|
|
14
|
+
if (value instanceof ArrayBuffer)
|
|
15
|
+
return value.byteLength;
|
|
16
|
+
if (ArrayBuffer.isView(value))
|
|
17
|
+
return value.byteLength;
|
|
18
|
+
if (typeof value === 'string')
|
|
19
|
+
return textEncoder.encode(value).byteLength;
|
|
20
|
+
try {
|
|
21
|
+
return textEncoder.encode(JSON.stringify(value)).byteLength;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function recordTrafficMetric(metrics, event) {
|
|
28
|
+
const uploadBytes = normaliseByteCount(event.uploadBytes);
|
|
29
|
+
const downloadBytes = normaliseByteCount(event.downloadBytes);
|
|
30
|
+
if (uploadBytes === 0 && downloadBytes === 0)
|
|
31
|
+
return metrics;
|
|
32
|
+
const currentSource = metrics.bySource[event.source] ?? { uploadBytes: 0, downloadBytes: 0 };
|
|
33
|
+
return {
|
|
34
|
+
uploadBytes: metrics.uploadBytes + uploadBytes,
|
|
35
|
+
downloadBytes: metrics.downloadBytes + downloadBytes,
|
|
36
|
+
bySource: {
|
|
37
|
+
...metrics.bySource,
|
|
38
|
+
[event.source]: {
|
|
39
|
+
uploadBytes: currentSource.uploadBytes + uploadBytes,
|
|
40
|
+
downloadBytes: currentSource.downloadBytes + downloadBytes,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function resetTrafficUpload(metrics, source) {
|
|
46
|
+
return resetTrafficDirection(metrics, 'upload', source);
|
|
47
|
+
}
|
|
48
|
+
export function resetTrafficDownload(metrics, source) {
|
|
49
|
+
return resetTrafficDirection(metrics, 'download', source);
|
|
50
|
+
}
|
|
51
|
+
export function resetTrafficAll(metrics, source) {
|
|
52
|
+
if (!source)
|
|
53
|
+
return createEmptyTrafficMetrics();
|
|
54
|
+
const sourceMetrics = metrics.bySource[source] ?? { uploadBytes: 0, downloadBytes: 0 };
|
|
55
|
+
const bySource = { ...metrics.bySource };
|
|
56
|
+
delete bySource[source];
|
|
57
|
+
return {
|
|
58
|
+
uploadBytes: metrics.uploadBytes - sourceMetrics.uploadBytes,
|
|
59
|
+
downloadBytes: metrics.downloadBytes - sourceMetrics.downloadBytes,
|
|
60
|
+
bySource,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function resetTrafficDirection(metrics, direction, source) {
|
|
64
|
+
const key = direction === 'upload' ? 'uploadBytes' : 'downloadBytes';
|
|
65
|
+
if (!source) {
|
|
66
|
+
const bySource = Object.fromEntries(Object.entries(metrics.bySource).map(([sourceName, sourceMetrics]) => [
|
|
67
|
+
sourceName,
|
|
68
|
+
{
|
|
69
|
+
...sourceMetrics,
|
|
70
|
+
[key]: 0,
|
|
71
|
+
},
|
|
72
|
+
]));
|
|
73
|
+
return {
|
|
74
|
+
...metrics,
|
|
75
|
+
[key]: 0,
|
|
76
|
+
bySource,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const sourceMetrics = metrics.bySource[source];
|
|
80
|
+
if (!sourceMetrics)
|
|
81
|
+
return metrics;
|
|
82
|
+
return {
|
|
83
|
+
...metrics,
|
|
84
|
+
[key]: metrics[key] - sourceMetrics[key],
|
|
85
|
+
bySource: {
|
|
86
|
+
...metrics.bySource,
|
|
87
|
+
[source]: {
|
|
88
|
+
...sourceMetrics,
|
|
89
|
+
[key]: 0,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function normaliseByteCount(value) {
|
|
95
|
+
if (!Number.isFinite(value) || value === undefined || value <= 0)
|
|
96
|
+
return 0;
|
|
97
|
+
return Math.floor(value);
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=traffic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traffic.js","sourceRoot":"","sources":["../src/traffic.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/actions/kv.test.ts
CHANGED
|
@@ -181,6 +181,65 @@ describe('kvConsolidateState', () => {
|
|
|
181
181
|
expect(result.subscriptions.has('Pair(bucket, key1)')).toBe(true)
|
|
182
182
|
})
|
|
183
183
|
|
|
184
|
+
it('should report KV watch entry download bytes', async () => {
|
|
185
|
+
const rawValue = '{"value":"hello"}'
|
|
186
|
+
let delivered = false
|
|
187
|
+
let resolveIterator: () => void
|
|
188
|
+
const iteratorDone = new Promise<void>((resolve) => {
|
|
189
|
+
resolveIterator = resolve
|
|
190
|
+
})
|
|
191
|
+
const mockWatcher = {
|
|
192
|
+
[Symbol.asyncIterator]: () => ({
|
|
193
|
+
next: () => {
|
|
194
|
+
if (!delivered) {
|
|
195
|
+
delivered = true
|
|
196
|
+
return Promise.resolve({
|
|
197
|
+
value: {
|
|
198
|
+
operation: 'PUT',
|
|
199
|
+
string: () => rawValue,
|
|
200
|
+
},
|
|
201
|
+
done: false,
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
resolveIterator()
|
|
205
|
+
return new Promise(() => {})
|
|
206
|
+
},
|
|
207
|
+
}),
|
|
208
|
+
}
|
|
209
|
+
const mockKv = {
|
|
210
|
+
watch: vi.fn().mockResolvedValue(mockWatcher),
|
|
211
|
+
}
|
|
212
|
+
const mockKvm = {
|
|
213
|
+
open: vi.fn().mockResolvedValue(mockKv),
|
|
214
|
+
}
|
|
215
|
+
const onDownloadBytes = vi.fn()
|
|
216
|
+
|
|
217
|
+
const actor = createActor(kvConsolidateState, {
|
|
218
|
+
input: {
|
|
219
|
+
kvm: mockKvm as any,
|
|
220
|
+
connection: {} as any,
|
|
221
|
+
currentState: new Map(),
|
|
222
|
+
targetState: new Map([
|
|
223
|
+
[
|
|
224
|
+
'Pair(bucket, key1)',
|
|
225
|
+
{
|
|
226
|
+
bucket: 'bucket',
|
|
227
|
+
key: 'key1',
|
|
228
|
+
callback: vi.fn(),
|
|
229
|
+
onDownloadBytes,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
]) as any,
|
|
233
|
+
},
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
actor.start()
|
|
237
|
+
await iteratorDone
|
|
238
|
+
await new Promise((r) => setTimeout(r, 10))
|
|
239
|
+
expect(onDownloadBytes).toHaveBeenCalledWith(new TextEncoder().encode(rawValue).byteLength)
|
|
240
|
+
actor.stop()
|
|
241
|
+
})
|
|
242
|
+
|
|
184
243
|
it('should keep existing subscriptions that are still in target', async () => {
|
|
185
244
|
const existingWatcher = { stop: vi.fn() } as any
|
|
186
245
|
const currentState = new Map([['Pair(bucket, key1)', existingWatcher]])
|
package/src/actions/kv.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Kvm, KvWatchEntry, KvWatchOptions } from '@nats-io/kv'
|
|
|
3
3
|
import { Pair } from '../utils'
|
|
4
4
|
import { fromPromise } from 'xstate'
|
|
5
5
|
import { recordError, withSpan } from '../telemetry'
|
|
6
|
+
import { byteLength } from '../traffic'
|
|
6
7
|
|
|
7
8
|
export class KvSubscriptionKey extends Pair<string, string> {}
|
|
8
9
|
|
|
@@ -12,6 +13,7 @@ export type KvSubscriptionConfig = {
|
|
|
12
13
|
callback: (data: any) => void
|
|
13
14
|
opts?: KvWatchOptions
|
|
14
15
|
replayOnReconnect?: boolean
|
|
16
|
+
onDownloadBytes?: (bytes: number) => void
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export const kvConsolidateState = fromPromise(
|
|
@@ -80,13 +82,19 @@ export const kvConsolidateState = fromPromise(
|
|
|
80
82
|
},
|
|
81
83
|
(span) => {
|
|
82
84
|
let parsedValue
|
|
85
|
+
let rawValue: string
|
|
83
86
|
try {
|
|
84
|
-
|
|
87
|
+
rawValue = e.string()
|
|
88
|
+
parsedValue = JSON.parse(rawValue)
|
|
85
89
|
} catch {
|
|
86
|
-
|
|
90
|
+
rawValue = e.string()
|
|
91
|
+
parsedValue = rawValue
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
try {
|
|
95
|
+
// KvWatchEntry does not expose the raw wire payload on this path; the
|
|
96
|
+
// UTF-8 byte length of string() is the measurable application payload.
|
|
97
|
+
config.onDownloadBytes?.(byteLength(rawValue))
|
|
90
98
|
config.callback({
|
|
91
99
|
bucket: config.bucket,
|
|
92
100
|
key: config.key,
|
|
@@ -199,6 +199,47 @@ describe('subjectConsolidateState', () => {
|
|
|
199
199
|
expect(callback).toHaveBeenCalledWith({ id: 2 })
|
|
200
200
|
})
|
|
201
201
|
|
|
202
|
+
it('should report subscription message download bytes', async () => {
|
|
203
|
+
const connection = createMockConnection()
|
|
204
|
+
const payload = new TextEncoder().encode('{"id":1}')
|
|
205
|
+
let resolveIterator: () => void
|
|
206
|
+
const iteratorDone = new Promise<void>((r) => (resolveIterator = r))
|
|
207
|
+
let delivered = false
|
|
208
|
+
|
|
209
|
+
const mockSub = {
|
|
210
|
+
unsubscribe: vi.fn(),
|
|
211
|
+
[Symbol.asyncIterator]: () => ({
|
|
212
|
+
next: () => {
|
|
213
|
+
if (!delivered) {
|
|
214
|
+
delivered = true
|
|
215
|
+
return Promise.resolve({
|
|
216
|
+
value: { data: payload, json: () => ({ id: 1 }), string: () => '{"id":1}' },
|
|
217
|
+
done: false,
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
resolveIterator!()
|
|
221
|
+
return new Promise(() => {})
|
|
222
|
+
},
|
|
223
|
+
}),
|
|
224
|
+
} as any
|
|
225
|
+
connection.subscribe.mockReturnValue(mockSub)
|
|
226
|
+
|
|
227
|
+
const onDownloadBytes = vi.fn()
|
|
228
|
+
subjectConsolidateState({
|
|
229
|
+
input: {
|
|
230
|
+
connection,
|
|
231
|
+
currentSubscriptions: new Map(),
|
|
232
|
+
targetSubscriptions: new Map([
|
|
233
|
+
['test.subject', { subject: 'test.subject', callback: vi.fn(), onDownloadBytes }],
|
|
234
|
+
]),
|
|
235
|
+
},
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
await iteratorDone
|
|
239
|
+
await new Promise((r) => setTimeout(r, 10))
|
|
240
|
+
expect(onDownloadBytes).toHaveBeenCalledWith(payload.byteLength)
|
|
241
|
+
})
|
|
242
|
+
|
|
202
243
|
it('should handle callback errors in the message loop', async () => {
|
|
203
244
|
const connection = createMockConnection()
|
|
204
245
|
let resolveIterator: () => void
|
package/src/actions/subject.ts
CHANGED
|
@@ -13,11 +13,13 @@ import {
|
|
|
13
13
|
recordError,
|
|
14
14
|
withSpan,
|
|
15
15
|
} from '../telemetry'
|
|
16
|
+
import { byteLength } from '../traffic'
|
|
16
17
|
|
|
17
18
|
export type SubjectSubscriptionConfig = {
|
|
18
19
|
subject: string
|
|
19
20
|
callback: (data: any) => void
|
|
20
21
|
opts?: SubscriptionOptions
|
|
22
|
+
onDownloadBytes?: (bytes: number) => void
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export type RequestResult = { ok: true } | { ok: false; error: Error }
|
|
@@ -70,15 +72,17 @@ export const subjectConsolidateState = ({
|
|
|
70
72
|
try {
|
|
71
73
|
for await (const msg of sub) {
|
|
72
74
|
const parentCtx = extractContextFromHeaders((msg as Msg).headers)
|
|
75
|
+
const payloadBytes = (msg as Msg).data?.length ?? 0
|
|
73
76
|
await withSpan(
|
|
74
77
|
'xstate.nats.message',
|
|
75
78
|
'xstate.nats.error',
|
|
76
79
|
{
|
|
77
80
|
subject,
|
|
78
|
-
'payload.bytes':
|
|
81
|
+
'payload.bytes': payloadBytes,
|
|
79
82
|
},
|
|
80
83
|
(span) => {
|
|
81
84
|
try {
|
|
85
|
+
subscriptionConfig.onDownloadBytes?.(payloadBytes)
|
|
82
86
|
subscriptionConfig?.callback(parseNatsResult(msg))
|
|
83
87
|
} catch (callbackError) {
|
|
84
88
|
// Record on span AND preserve the existing console.error
|
|
@@ -117,19 +121,15 @@ export const subjectRequest = ({
|
|
|
117
121
|
opts?: RequestOptions
|
|
118
122
|
callback: (data: any) => void
|
|
119
123
|
onRequestResult?: (result: RequestResult) => void
|
|
124
|
+
onDownloadBytes?: (bytes: number) => void
|
|
120
125
|
}
|
|
121
126
|
}) => {
|
|
122
|
-
const { connection, subject, payload, opts, callback, onRequestResult } = input
|
|
127
|
+
const { connection, subject, payload, opts, callback, onRequestResult, onDownloadBytes } = input
|
|
123
128
|
if (!connection) {
|
|
124
129
|
throw new Error('NATS connection is not available')
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
const payloadBytes =
|
|
128
|
-
payload instanceof Uint8Array
|
|
129
|
-
? payload.byteLength
|
|
130
|
-
: typeof payload === 'string'
|
|
131
|
-
? payload.length
|
|
132
|
-
: undefined
|
|
132
|
+
const payloadBytes = byteLength(payload)
|
|
133
133
|
|
|
134
134
|
void withSpan(
|
|
135
135
|
'xstate.nats.request',
|
|
@@ -151,6 +151,7 @@ export const subjectRequest = ({
|
|
|
151
151
|
return connection
|
|
152
152
|
.request(subject, payload, requestOpts)
|
|
153
153
|
.then((msg: Msg) => {
|
|
154
|
+
onDownloadBytes?.(msg.data?.length ?? 0)
|
|
154
155
|
callback(parseNatsResult(msg))
|
|
155
156
|
onRequestResult?.({ ok: true })
|
|
156
157
|
})
|
|
@@ -185,12 +186,7 @@ export const subjectPublish = ({
|
|
|
185
186
|
throw new Error('NATS connection is not available')
|
|
186
187
|
}
|
|
187
188
|
|
|
188
|
-
const payloadBytes =
|
|
189
|
-
payload instanceof Uint8Array
|
|
190
|
-
? payload.byteLength
|
|
191
|
-
: typeof payload === 'string'
|
|
192
|
-
? payload.length
|
|
193
|
-
: undefined
|
|
189
|
+
const payloadBytes = byteLength(payload)
|
|
194
190
|
|
|
195
191
|
try {
|
|
196
192
|
withSpan(
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,20 @@ export {
|
|
|
12
12
|
export { KvSubscriptionKey, type KvSubscriptionConfig } from './actions/kv'
|
|
13
13
|
export { parseNatsResult } from './actions/connection'
|
|
14
14
|
export { type AuthConfig } from './actions/types'
|
|
15
|
+
export {
|
|
16
|
+
byteLength as natsTrafficByteLength,
|
|
17
|
+
createEmptyTrafficMetrics,
|
|
18
|
+
recordTrafficMetric,
|
|
19
|
+
resetTrafficAll,
|
|
20
|
+
resetTrafficDownload,
|
|
21
|
+
resetTrafficUpload,
|
|
22
|
+
type NatsTrafficEvent,
|
|
23
|
+
type NatsTrafficMetricBucket,
|
|
24
|
+
type NatsTrafficMetrics,
|
|
25
|
+
type NatsTrafficMetricSource,
|
|
26
|
+
type NatsTrafficRecordEvent,
|
|
27
|
+
type NatsTrafficResetEvent,
|
|
28
|
+
} from './traffic'
|
|
15
29
|
|
|
16
30
|
export {
|
|
17
31
|
type NatsConnectionConfig,
|