@omnitronix/rng-client-core 1.0.10 → 1.1.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/dist/core/rng-client-core.d.ts +0 -1
- package/dist/core/rng-client-core.js +1 -1
- package/dist/errors/rng-errors.d.ts +0 -1
- package/dist/errors/rng-errors.js +1 -1
- package/dist/grpc/grpc-rng-client.d.ts +0 -1
- package/dist/grpc/grpc-rng-client.js +1 -1
- package/dist/grpc/grpc-rng-interface.d.ts +0 -1
- package/dist/grpc/grpc-rng-interface.js +1 -1
- package/dist/http/http-client.types.d.ts +0 -1
- package/dist/http/http-client.types.js +1 -1
- package/dist/http/http-rng-client.d.ts +0 -1
- package/dist/http/http-rng-client.js +1 -1
- package/dist/http/rest-client.d.ts +0 -1
- package/dist/http/rest-client.js +1 -1
- package/dist/http/rng-client.interface.d.ts +0 -1
- package/dist/http/rng-client.interface.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +1 -1
- package/dist/interfaces/client-interfaces.d.ts +6 -3
- package/dist/interfaces/client-interfaces.js +1 -1
- package/dist/interfaces/metrics.d.ts +30 -0
- package/dist/interfaces/metrics.js +3 -0
- package/dist/metrics/default-metrics-handlers.d.ts +2 -0
- package/dist/metrics/default-metrics-handlers.js +23 -0
- package/dist/ring-buffer/interfaces.d.ts +0 -38
- package/dist/ring-buffer/interfaces.js +1 -1
- package/dist/ring-buffer/ring-buffer.d.ts +7 -14
- package/dist/ring-buffer/ring-buffer.js +63 -207
- package/dist/ring-buffer/ring-storage-memory.d.ts +1 -3
- package/dist/ring-buffer/ring-storage-memory.js +1 -25
- package/dist/ring-buffer/ring-storage.d.ts +1 -3
- package/dist/ring-buffer/ring-storage.js +1 -7
- package/dist/rng-client.d.ts +2 -5
- package/dist/rng-client.js +6 -4
- package/package.json +3 -2
- package/src/__tests__/grpc-rng-client.test.ts +167 -0
- package/src/__tests__/rng-client-core.integration.test.ts +117 -0
- package/src/__tests__/rng-client-core.test.ts +249 -0
- package/src/__tests__/rng-client-grpc.test.ts +324 -0
- package/src/__tests__/rng-client.test.ts +296 -0
- package/src/core/rng-client-core.ts +80 -0
- package/src/errors/rng-errors.ts +96 -0
- package/src/grpc/grpc-rng-client.ts +162 -0
- package/src/grpc/grpc-rng-interface.ts +36 -0
- package/src/http/http-client.types.ts +48 -0
- package/src/http/http-rng-client.ts +140 -0
- package/src/http/rest-client.ts +151 -0
- package/src/http/rng-client.interface.ts +22 -0
- package/src/index.ts +21 -0
- package/src/interfaces/client-interfaces.ts +15 -0
- package/src/interfaces/metrics.ts +38 -0
- package/src/metrics/default-metrics-handlers.ts +29 -0
- package/src/ring-buffer/interfaces.ts +9 -0
- package/src/ring-buffer/ring-buffer.ts +214 -0
- package/src/ring-buffer/ring-storage-memory.ts +48 -0
- package/src/ring-buffer/ring-storage.ts +59 -0
- package/src/rng-client.ts +190 -0
- package/src/rng.proto +102 -0
- package/dist/core/rng-client-core.d.ts.map +0 -1
- package/dist/core/rng-client-core.js.map +0 -1
- package/dist/errors/rng-errors.d.ts.map +0 -1
- package/dist/errors/rng-errors.js.map +0 -1
- package/dist/grpc/grpc-rng-client.d.ts.map +0 -1
- package/dist/grpc/grpc-rng-client.js.map +0 -1
- package/dist/grpc/grpc-rng-interface.d.ts.map +0 -1
- package/dist/grpc/grpc-rng-interface.js.map +0 -1
- package/dist/http/http-client.types.d.ts.map +0 -1
- package/dist/http/http-client.types.js.map +0 -1
- package/dist/http/http-rng-client.d.ts.map +0 -1
- package/dist/http/http-rng-client.js.map +0 -1
- package/dist/http/rest-client.d.ts.map +0 -1
- package/dist/http/rest-client.js.map +0 -1
- package/dist/http/rng-client.interface.d.ts.map +0 -1
- package/dist/http/rng-client.interface.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/client-interfaces.d.ts.map +0 -1
- package/dist/interfaces/client-interfaces.js.map +0 -1
- package/dist/ring-buffer/interfaces.d.ts.map +0 -1
- package/dist/ring-buffer/interfaces.js.map +0 -1
- package/dist/ring-buffer/ring-buffer.d.ts.map +0 -1
- package/dist/ring-buffer/ring-buffer.js.map +0 -1
- package/dist/ring-buffer/ring-storage-memory.d.ts.map +0 -1
- package/dist/ring-buffer/ring-storage-memory.js.map +0 -1
- package/dist/ring-buffer/ring-storage.d.ts.map +0 -1
- package/dist/ring-buffer/ring-storage.js.map +0 -1
- package/dist/rng-client.d.ts.map +0 -1
- package/dist/rng-client.js.map +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface Counter<L = Record<string, string | number>> {
|
|
2
|
+
inc: (_labels?: L, _value?: number) => void;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface Histogram<L = Record<string, string | number>> {
|
|
6
|
+
observe: (_labels: L, _value: number) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Gauge<L = Record<string, string | number>> {
|
|
10
|
+
set: (_labels: L, _value: number) => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface RngBufferBaseLabels {
|
|
14
|
+
rng_range: string;
|
|
15
|
+
engine_type: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RngBufferMetricsHandlers {
|
|
19
|
+
rngBufferCapacity: Gauge<RngBufferBaseLabels>;
|
|
20
|
+
rngBufferFillLevel: Gauge<RngBufferBaseLabels>;
|
|
21
|
+
rngBufferFillRatio: Gauge<RngBufferBaseLabels>;
|
|
22
|
+
rngBufferRefillRequestsTotal: Counter<
|
|
23
|
+
RngBufferBaseLabels & {
|
|
24
|
+
reason?: string;
|
|
25
|
+
}
|
|
26
|
+
>;
|
|
27
|
+
rngBufferRefillFailuresTotal: Counter<
|
|
28
|
+
RngBufferBaseLabels & {
|
|
29
|
+
reason?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
>;
|
|
33
|
+
rngBufferRefillDurationMs: Histogram<RngBufferBaseLabels>;
|
|
34
|
+
rngBufferStarvationEventsTotal: Counter<RngBufferBaseLabels>;
|
|
35
|
+
rngBufferLatencyMs: Histogram<RngBufferBaseLabels>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type RngClientMetricsHandlers = RngBufferMetricsHandlers;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Counter,
|
|
3
|
+
Gauge,
|
|
4
|
+
Histogram,
|
|
5
|
+
RngClientMetricsHandlers,
|
|
6
|
+
} from '../interfaces/metrics';
|
|
7
|
+
|
|
8
|
+
class DummyCounter implements Counter {
|
|
9
|
+
inc(_labels?: unknown, _value?: number): void {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class DummyGauge implements Gauge {
|
|
13
|
+
set(_labels: unknown, _value: number): void {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class DummyHistogram implements Histogram {
|
|
17
|
+
observe(_labels: unknown, _value: number): void {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const defaultMetricsHandlers: RngClientMetricsHandlers = {
|
|
21
|
+
rngBufferCapacity: new DummyGauge(),
|
|
22
|
+
rngBufferFillLevel: new DummyGauge(),
|
|
23
|
+
rngBufferFillRatio: new DummyGauge(),
|
|
24
|
+
rngBufferRefillRequestsTotal: new DummyCounter(),
|
|
25
|
+
rngBufferRefillFailuresTotal: new DummyCounter(),
|
|
26
|
+
rngBufferRefillDurationMs: new DummyHistogram(),
|
|
27
|
+
rngBufferStarvationEventsTotal: new DummyCounter(),
|
|
28
|
+
rngBufferLatencyMs: new DummyHistogram(),
|
|
29
|
+
};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { RingBufferValue, SingleRingBuffer } from './interfaces';
|
|
2
|
+
import { RngClientCore } from '../core/rng-client-core';
|
|
3
|
+
import { RingStorage, StorageType } from './ring-storage';
|
|
4
|
+
import { RngBufferMetricsHandlers } from '../interfaces/metrics';
|
|
5
|
+
|
|
6
|
+
export const FILL_UP_BUFFER_DELAY = 5;
|
|
7
|
+
|
|
8
|
+
export interface RingBufferConfig {
|
|
9
|
+
size?: number;
|
|
10
|
+
clientCore: RngClientCore;
|
|
11
|
+
storageType: StorageType;
|
|
12
|
+
metrics: RngBufferMetricsHandlers;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class RingBuffer {
|
|
16
|
+
private size: number;
|
|
17
|
+
private maxSize: number;
|
|
18
|
+
private clientCore: RngClientCore;
|
|
19
|
+
private storageType: StorageType;
|
|
20
|
+
private storage: RingStorage;
|
|
21
|
+
private readonly metrics: RngBufferMetricsHandlers;
|
|
22
|
+
|
|
23
|
+
constructor({ size, clientCore, storageType, metrics }: RingBufferConfig) {
|
|
24
|
+
this.size = size ?? 2000;
|
|
25
|
+
this.maxSize = this.size * 2;
|
|
26
|
+
this.clientCore = clientCore;
|
|
27
|
+
this.storageType = storageType;
|
|
28
|
+
this.storage = new RingStorage(storageType);
|
|
29
|
+
this.metrics = metrics;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private getRangeKey(min: number, max: number): string {
|
|
33
|
+
return `${min}-${max}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private getEngineType(): string {
|
|
37
|
+
return 'default';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async getNumbersByRange(
|
|
41
|
+
min: number,
|
|
42
|
+
max: number,
|
|
43
|
+
count = 1
|
|
44
|
+
): Promise<RingBufferValue[] | undefined> {
|
|
45
|
+
const rangeKey = this.getRangeKey(min, max);
|
|
46
|
+
let requestStartTime = Date.now();
|
|
47
|
+
|
|
48
|
+
const buffer = await this.storage.getBufferByRange(min, max);
|
|
49
|
+
if (!buffer) {
|
|
50
|
+
this.metrics.rngBufferStarvationEventsTotal.inc({
|
|
51
|
+
rng_range: rangeKey,
|
|
52
|
+
engine_type: this.getEngineType(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const newBuffer = this.createBuffer(min, max);
|
|
56
|
+
await this.storage.saveBuffer(newBuffer);
|
|
57
|
+
setTimeout(async () => {
|
|
58
|
+
requestStartTime = Date.now();
|
|
59
|
+
await this.fillUpBuffer(newBuffer, 'initial');
|
|
60
|
+
const latency = Date.now() - requestStartTime;
|
|
61
|
+
this.metrics.rngBufferLatencyMs.observe(
|
|
62
|
+
{
|
|
63
|
+
rng_range: rangeKey,
|
|
64
|
+
engine_type: this.getEngineType(),
|
|
65
|
+
},
|
|
66
|
+
latency
|
|
67
|
+
);
|
|
68
|
+
}, FILL_UP_BUFFER_DELAY);
|
|
69
|
+
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const item = await this.storage.getFromBuffer(buffer, count);
|
|
74
|
+
if (!item) {
|
|
75
|
+
this.metrics.rngBufferStarvationEventsTotal.inc({
|
|
76
|
+
rng_range: rangeKey,
|
|
77
|
+
engine_type: this.getEngineType(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
setTimeout(async () => {
|
|
81
|
+
requestStartTime = Date.now();
|
|
82
|
+
await this.fillUpBuffer(buffer, 'starvation');
|
|
83
|
+
const latency = Date.now() - requestStartTime;
|
|
84
|
+
this.metrics.rngBufferLatencyMs.observe(
|
|
85
|
+
{
|
|
86
|
+
rng_range: rangeKey,
|
|
87
|
+
engine_type: this.getEngineType(),
|
|
88
|
+
},
|
|
89
|
+
latency
|
|
90
|
+
);
|
|
91
|
+
}, FILL_UP_BUFFER_DELAY);
|
|
92
|
+
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setTimeout(async () => {
|
|
97
|
+
requestStartTime = Date.now();
|
|
98
|
+
await this.fillUpBuffer(buffer, 'refill');
|
|
99
|
+
const latency = Date.now() - requestStartTime;
|
|
100
|
+
this.metrics.rngBufferLatencyMs.observe(
|
|
101
|
+
{
|
|
102
|
+
rng_range: rangeKey,
|
|
103
|
+
engine_type: this.getEngineType(),
|
|
104
|
+
},
|
|
105
|
+
latency
|
|
106
|
+
);
|
|
107
|
+
}, FILL_UP_BUFFER_DELAY);
|
|
108
|
+
|
|
109
|
+
return item;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async fillUpBuffer(
|
|
113
|
+
buffer: SingleRingBuffer,
|
|
114
|
+
reason: string = 'scheduled'
|
|
115
|
+
): Promise<void> {
|
|
116
|
+
const rangeKey = this.getRangeKey(buffer.range.min, buffer.range.max);
|
|
117
|
+
const refillStartTime = Date.now();
|
|
118
|
+
|
|
119
|
+
this.metrics.rngBufferRefillRequestsTotal.inc({
|
|
120
|
+
rng_range: rangeKey,
|
|
121
|
+
engine_type: this.getEngineType(),
|
|
122
|
+
reason,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const values = await this.pullNumberByBuffer(buffer);
|
|
127
|
+
if (values.length === 0 || buffer.items.length >= this.maxSize) {
|
|
128
|
+
const duration = Date.now() - refillStartTime;
|
|
129
|
+
this.metrics.rngBufferRefillDurationMs.observe(
|
|
130
|
+
{
|
|
131
|
+
rng_range: rangeKey,
|
|
132
|
+
engine_type: this.getEngineType(),
|
|
133
|
+
},
|
|
134
|
+
duration
|
|
135
|
+
);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
await this.storage.addItemsToBuffer(buffer, values);
|
|
139
|
+
this.metrics.rngBufferFillLevel.set(
|
|
140
|
+
{
|
|
141
|
+
rng_range: rangeKey,
|
|
142
|
+
engine_type: this.getEngineType(),
|
|
143
|
+
},
|
|
144
|
+
buffer.items.length
|
|
145
|
+
);
|
|
146
|
+
this.metrics.rngBufferFillRatio.set(
|
|
147
|
+
{
|
|
148
|
+
rng_range: rangeKey,
|
|
149
|
+
engine_type: this.getEngineType(),
|
|
150
|
+
},
|
|
151
|
+
buffer.items.length / this.size
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const duration = Date.now() - refillStartTime;
|
|
155
|
+
this.metrics.rngBufferRefillDurationMs.observe(
|
|
156
|
+
{
|
|
157
|
+
rng_range: rangeKey,
|
|
158
|
+
engine_type: this.getEngineType(),
|
|
159
|
+
},
|
|
160
|
+
duration
|
|
161
|
+
);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
const errorMessage =
|
|
164
|
+
error instanceof Error ? error.message : String(error);
|
|
165
|
+
this.metrics.rngBufferRefillFailuresTotal.inc({
|
|
166
|
+
rng_range: rangeKey,
|
|
167
|
+
engine_type: this.getEngineType(),
|
|
168
|
+
reason,
|
|
169
|
+
error: errorMessage,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const duration = Date.now() - refillStartTime;
|
|
173
|
+
this.metrics.rngBufferRefillDurationMs.observe(
|
|
174
|
+
{
|
|
175
|
+
rng_range: rangeKey,
|
|
176
|
+
engine_type: this.getEngineType(),
|
|
177
|
+
},
|
|
178
|
+
duration
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async pullNumberByBuffer(
|
|
186
|
+
buffer: SingleRingBuffer
|
|
187
|
+
): Promise<RingBufferValue[]> {
|
|
188
|
+
const count = this.size - buffer.items.length;
|
|
189
|
+
if (count <= 0) {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
const values = await this.clientCore.getBatchNumbersWithSeeds({
|
|
193
|
+
min: buffer.range.min,
|
|
194
|
+
max: buffer.range.max,
|
|
195
|
+
count,
|
|
196
|
+
});
|
|
197
|
+
return values.result as RingBufferValue[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
createBuffer(min: number, max: number): SingleRingBuffer {
|
|
201
|
+
this.metrics.rngBufferCapacity.set(
|
|
202
|
+
{
|
|
203
|
+
rng_range: this.getRangeKey(min, max),
|
|
204
|
+
engine_type: this.getEngineType(),
|
|
205
|
+
},
|
|
206
|
+
this.size
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
items: [],
|
|
211
|
+
range: { min, max },
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { RingBufferValue, SingleRingBuffer } from './interfaces';
|
|
2
|
+
|
|
3
|
+
export class RingStorageMemory {
|
|
4
|
+
private storage: { [key: string]: SingleRingBuffer };
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
this.storage = {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getBufferName(min: number, max: number): string {
|
|
11
|
+
return `${min}-${max}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async saveBuffer(buffer: SingleRingBuffer): Promise<void> {
|
|
15
|
+
this.storage[this.getBufferName(buffer.range.min, buffer.range.max)] =
|
|
16
|
+
buffer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getBuffer(name: string): Promise<SingleRingBuffer> {
|
|
20
|
+
return this.storage[name];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async getFromBuffer(
|
|
24
|
+
name: string,
|
|
25
|
+
count = 1
|
|
26
|
+
): Promise<RingBufferValue[] | undefined> {
|
|
27
|
+
const buffer = await this.getBuffer(name);
|
|
28
|
+
if (!buffer) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
if (buffer.items.length < count) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const items = buffer.items.splice(0, count);
|
|
35
|
+
if (items.length === 0) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return items;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async addItemsToBuffer(
|
|
42
|
+
buffer: SingleRingBuffer,
|
|
43
|
+
items: RingBufferValue[]
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
buffer.items.push(...items);
|
|
46
|
+
await this.saveBuffer(buffer);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { RingBufferValue, SingleRingBuffer } from './interfaces';
|
|
2
|
+
import { RingStorageMemory } from './ring-storage-memory';
|
|
3
|
+
|
|
4
|
+
export enum StorageType {
|
|
5
|
+
IN_MEMORY = 'in_memory',
|
|
6
|
+
REDIS = 'redis',
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export class RingStorage {
|
|
10
|
+
private storage: RingStorageMemory;
|
|
11
|
+
constructor(private readonly storageType: StorageType) {
|
|
12
|
+
this.storage = new RingStorageMemory();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async getFromBuffer(
|
|
16
|
+
buffer: SingleRingBuffer,
|
|
17
|
+
count = 1
|
|
18
|
+
): Promise<RingBufferValue[] | undefined> {
|
|
19
|
+
if (this.storageType === StorageType.IN_MEMORY) {
|
|
20
|
+
return this.storage.getFromBuffer(
|
|
21
|
+
this.storage.getBufferName(buffer.range.min, buffer.range.max),
|
|
22
|
+
count
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async saveBuffer(buffer: SingleRingBuffer): Promise<void> {
|
|
29
|
+
if (this.storageType === StorageType.IN_MEMORY) {
|
|
30
|
+
this.storage.saveBuffer(buffer);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async getBufferByRange(
|
|
35
|
+
min: number,
|
|
36
|
+
max: number
|
|
37
|
+
): Promise<SingleRingBuffer | undefined> {
|
|
38
|
+
if (this.storageType === StorageType.IN_MEMORY) {
|
|
39
|
+
return this.storage.getBuffer(this.storage.getBufferName(min, max));
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async getBuffer(name: string): Promise<SingleRingBuffer | undefined> {
|
|
45
|
+
if (this.storageType === StorageType.IN_MEMORY) {
|
|
46
|
+
return this.storage.getBuffer(name);
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async addItemsToBuffer(
|
|
52
|
+
buffer: SingleRingBuffer,
|
|
53
|
+
items: RingBufferValue[]
|
|
54
|
+
): Promise<void> {
|
|
55
|
+
if (this.storageType === StorageType.IN_MEMORY) {
|
|
56
|
+
this.storage.addItemsToBuffer(buffer, items);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { RngClientCore } from './core/rng-client-core';
|
|
2
|
+
import { RngClientConfig } from './interfaces/client-interfaces';
|
|
3
|
+
import { StorageType } from './ring-buffer/ring-storage';
|
|
4
|
+
import {
|
|
5
|
+
RngSingleResponse,
|
|
6
|
+
RngBatchResponse,
|
|
7
|
+
RngSingleRequest,
|
|
8
|
+
RngBatchRequest,
|
|
9
|
+
RngSingleFloatRequest,
|
|
10
|
+
RngBatchFloatRequest,
|
|
11
|
+
} from './http/http-client.types';
|
|
12
|
+
import { RingBuffer } from './ring-buffer/ring-buffer';
|
|
13
|
+
import { defaultMetricsHandlers } from './metrics/default-metrics-handlers';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* RNG Client - High-level API for client-side random number generation
|
|
17
|
+
*
|
|
18
|
+
* This is the main entry point for client-side RNG operations.
|
|
19
|
+
* It provides a simple interface for generating random numbers
|
|
20
|
+
* via HTTP communication with the RNG service.
|
|
21
|
+
*/
|
|
22
|
+
export class RngClient {
|
|
23
|
+
private readonly clientCore: RngClientCore;
|
|
24
|
+
private readonly ringBuffer: RingBuffer;
|
|
25
|
+
|
|
26
|
+
constructor(config: RngClientConfig) {
|
|
27
|
+
this.clientCore = new RngClientCore(config);
|
|
28
|
+
this.ringBuffer = new RingBuffer({
|
|
29
|
+
size: config.poolingSize || 2000,
|
|
30
|
+
clientCore: this.clientCore,
|
|
31
|
+
storageType: config.storageType || StorageType.IN_MEMORY,
|
|
32
|
+
metrics: {
|
|
33
|
+
...defaultMetricsHandlers,
|
|
34
|
+
...config.metrics?.handlers,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get a single random number
|
|
41
|
+
* @param min Minimum value (inclusive)
|
|
42
|
+
* @param max Maximum value (inclusive)
|
|
43
|
+
* @param seed Optional seed for deterministic generation
|
|
44
|
+
* @returns Random integer result
|
|
45
|
+
*/
|
|
46
|
+
async getSingleNumber(
|
|
47
|
+
min: number,
|
|
48
|
+
max: number,
|
|
49
|
+
seed?: number
|
|
50
|
+
): Promise<{ value: number; seed: number }> {
|
|
51
|
+
const data: RngSingleRequest = {
|
|
52
|
+
min,
|
|
53
|
+
max,
|
|
54
|
+
};
|
|
55
|
+
if (seed) {
|
|
56
|
+
data.seed = seed;
|
|
57
|
+
} else {
|
|
58
|
+
const item = await this.ringBuffer.getNumbersByRange(min, max, 1);
|
|
59
|
+
if (item && item.length > 0) {
|
|
60
|
+
return { value: item[0].value, seed: item[0].seed };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const response = await this.clientCore.getSingleNumber(data);
|
|
64
|
+
return { value: response.result, seed: response.seed };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get a batch of random numbers
|
|
69
|
+
* @param min Minimum value (inclusive)
|
|
70
|
+
* @param max Maximum value (inclusive)
|
|
71
|
+
* @param count Number of values to generate
|
|
72
|
+
* @param seed Optional seed for deterministic generation
|
|
73
|
+
* @returns Batch of random integers
|
|
74
|
+
*/
|
|
75
|
+
async getBatchNumbers(
|
|
76
|
+
min: number,
|
|
77
|
+
max: number,
|
|
78
|
+
count: number,
|
|
79
|
+
seed?: number
|
|
80
|
+
): Promise<number[]> {
|
|
81
|
+
const data: RngBatchRequest = {
|
|
82
|
+
min,
|
|
83
|
+
max,
|
|
84
|
+
count,
|
|
85
|
+
};
|
|
86
|
+
if (seed) {
|
|
87
|
+
data.seed = seed;
|
|
88
|
+
}
|
|
89
|
+
const response = await this.clientCore.getBatchNumbers(data);
|
|
90
|
+
|
|
91
|
+
return response.result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get a batch of random numbers with seeds
|
|
96
|
+
* @param min Minimum value (inclusive)
|
|
97
|
+
* @param max Maximum value (inclusive)
|
|
98
|
+
* @param count Number of values to generate
|
|
99
|
+
* @param seed Optional seed for deterministic generation
|
|
100
|
+
* @returns Batch of random integers
|
|
101
|
+
*/
|
|
102
|
+
async getBatchNumbersWithSeeds(
|
|
103
|
+
min: number,
|
|
104
|
+
max: number,
|
|
105
|
+
count: number,
|
|
106
|
+
seed?: number
|
|
107
|
+
): Promise<{ value: number; seed: number }[]> {
|
|
108
|
+
const data: RngBatchRequest = {
|
|
109
|
+
min,
|
|
110
|
+
max,
|
|
111
|
+
count,
|
|
112
|
+
};
|
|
113
|
+
if (seed) {
|
|
114
|
+
data.seed = seed;
|
|
115
|
+
} else {
|
|
116
|
+
const item = await this.ringBuffer.getNumbersByRange(min, max, count);
|
|
117
|
+
if (item && item.length > 0) {
|
|
118
|
+
return item;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const response = await this.clientCore.getBatchNumbersWithSeeds(data);
|
|
122
|
+
|
|
123
|
+
return response.result.map((item) => ({
|
|
124
|
+
value: item.value,
|
|
125
|
+
seed: item.seed,
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get a single random float
|
|
131
|
+
* @param seed Optional seed for deterministic generation
|
|
132
|
+
* @returns Random float result
|
|
133
|
+
*/
|
|
134
|
+
async getSingleFloat(seed?: number): Promise<RngSingleResponse> {
|
|
135
|
+
const data: RngSingleFloatRequest = {};
|
|
136
|
+
if (seed) {
|
|
137
|
+
data.seed = seed;
|
|
138
|
+
}
|
|
139
|
+
return this.clientCore.getSingleFloat(data);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get a batch of random floats
|
|
144
|
+
* @param count Number of values to generate
|
|
145
|
+
* @param seed Optional seed for deterministic generation
|
|
146
|
+
* @returns Batch of random floats
|
|
147
|
+
*/
|
|
148
|
+
async getBatchFloats(
|
|
149
|
+
count: number,
|
|
150
|
+
seed?: number
|
|
151
|
+
): Promise<RngBatchResponse> {
|
|
152
|
+
const data: RngBatchFloatRequest = { count };
|
|
153
|
+
if (seed) {
|
|
154
|
+
data.seed = seed;
|
|
155
|
+
}
|
|
156
|
+
return this.clientCore.getBatchFloats(data);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get a random number
|
|
161
|
+
* @param min Minimum value (inclusive)
|
|
162
|
+
* @param max Maximum value (inclusive)
|
|
163
|
+
* @param seed Optional seed for deterministic generation
|
|
164
|
+
* @returns Random integer result
|
|
165
|
+
*/
|
|
166
|
+
async getRandomNumber(
|
|
167
|
+
min: number,
|
|
168
|
+
max: number,
|
|
169
|
+
seed?: number
|
|
170
|
+
): Promise<RngSingleResponse> {
|
|
171
|
+
const data: RngSingleRequest = { min, max };
|
|
172
|
+
if (seed) {
|
|
173
|
+
data.seed = seed;
|
|
174
|
+
}
|
|
175
|
+
return this.clientCore.getRandomNumber(data);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get a random float
|
|
180
|
+
* @param seed Optional seed for deterministic generation
|
|
181
|
+
* @returns Random float result
|
|
182
|
+
*/
|
|
183
|
+
async getRandomFloat(seed?: number): Promise<RngSingleResponse> {
|
|
184
|
+
const data: RngSingleFloatRequest = {};
|
|
185
|
+
if (seed) {
|
|
186
|
+
data.seed = seed;
|
|
187
|
+
}
|
|
188
|
+
return this.clientCore.getRandomFloat(data);
|
|
189
|
+
}
|
|
190
|
+
}
|
package/src/rng.proto
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package rng;
|
|
4
|
+
|
|
5
|
+
service RngService {
|
|
6
|
+
rpc Single (SingleRequest) returns (SingleResponse);
|
|
7
|
+
rpc SingleBatch (BatchRequest) returns (BatchResponse);
|
|
8
|
+
rpc SingleBatchWithSeeds (BatchRequest) returns (BatchWithSeedsResponse);
|
|
9
|
+
rpc Float (FloatRequest) returns (FloatResponse);
|
|
10
|
+
rpc FloatBatch (BatchFloatRequest) returns (BatchFloatResponse);
|
|
11
|
+
rpc FloatBatchWithSeeds (BatchFloatRequest) returns (BatchFloatWithSeedsResponse);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
message SingleRequest {
|
|
15
|
+
int32 min = 1;
|
|
16
|
+
int32 max = 2;
|
|
17
|
+
optional string sessionId = 3;
|
|
18
|
+
optional int32 seed = 4;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
message SingleResponse {
|
|
22
|
+
string id = 1;
|
|
23
|
+
int32 seed = 2;
|
|
24
|
+
int32 result = 3;
|
|
25
|
+
int32 min = 4;
|
|
26
|
+
int32 max = 5;
|
|
27
|
+
string createdAt = 6;
|
|
28
|
+
optional string sessionId = 7;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
message FloatRequest {
|
|
32
|
+
optional string sessionId = 1;
|
|
33
|
+
optional int32 seed = 2;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
message FloatResponse {
|
|
37
|
+
string id = 1;
|
|
38
|
+
int32 seed = 2;
|
|
39
|
+
double result = 3;
|
|
40
|
+
string createdAt = 4;
|
|
41
|
+
optional string sessionId = 5;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
message BatchFloatRequest {
|
|
45
|
+
int32 count = 1;
|
|
46
|
+
optional string sessionId = 2;
|
|
47
|
+
optional int32 seed = 3;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
message BatchFloatResponse {
|
|
51
|
+
string id = 1;
|
|
52
|
+
int32 seed = 2;
|
|
53
|
+
repeated double result = 3;
|
|
54
|
+
string createdAt = 4;
|
|
55
|
+
optional string sessionId = 5;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
message BatchFloatWithSeedsResponse {
|
|
59
|
+
string id = 1;
|
|
60
|
+
int32 seed = 2;
|
|
61
|
+
repeated BatchFloatItem result = 3;
|
|
62
|
+
string createdAt = 4;
|
|
63
|
+
optional string sessionId = 5;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
message BatchFloatItem {
|
|
67
|
+
double value = 1;
|
|
68
|
+
int32 seed = 2;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
message BatchRequest {
|
|
72
|
+
int32 min = 1;
|
|
73
|
+
int32 max = 2;
|
|
74
|
+
int32 count = 3;
|
|
75
|
+
optional string sessionId = 4;
|
|
76
|
+
optional int32 seed = 5;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
message BatchResponse {
|
|
80
|
+
string id = 1;
|
|
81
|
+
int32 seed = 2;
|
|
82
|
+
repeated int32 result = 3;
|
|
83
|
+
int32 min = 4;
|
|
84
|
+
int32 max = 5;
|
|
85
|
+
string createdAt = 6;
|
|
86
|
+
optional string sessionId = 7;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
message BatchWithSeedsResponse {
|
|
90
|
+
string id = 1;
|
|
91
|
+
int32 seed = 2;
|
|
92
|
+
repeated BatchItem result = 3;
|
|
93
|
+
int32 min = 4;
|
|
94
|
+
int32 max = 5;
|
|
95
|
+
string createdAt = 6;
|
|
96
|
+
optional string sessionId = 7;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
message BatchItem {
|
|
100
|
+
int32 value = 1;
|
|
101
|
+
uint64 seed = 2;
|
|
102
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rng-client-core.d.ts","sourceRoot":"","sources":["../../src/core/rng-client-core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAIlE,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,yBAAyB,EACzB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,2BAA2B,CAAC;AAEnC,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;gBAEnC,MAAM,EAAE,eAAe;IAUtB,eAAe,CAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,iBAAiB,CAAC;IAIhB,eAAe,CAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,iBAAiB,CAAC;IAIhB,cAAc,CACzB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,iBAAiB,CAAC;IAMhB,cAAc,CACzB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,iBAAiB,CAAC;IAMhB,cAAc,CACzB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,gBAAgB,CAAC;IAIf,eAAe,CAC1B,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,gBAAgB,CAAC;IAIf,wBAAwB,CACnC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,yBAAyB,CAAC;IAIxB,sBAAsB,CACjC,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,yBAAyB,CAAC;CAGtC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rng-client-core.js","sourceRoot":"","sources":["../../src/core/rng-client-core.ts"],"names":[],"mappings":";;;AAEA,6DAAwD;AACxD,6DAAwD;AAWxD,MAAa,aAAa;IAIxB,YAAY,MAAuB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,IAAI,MAAM,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,SAAS,GAAG,IAAI,+BAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrD,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,GAAG,IAAI,+BAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,eAAe,CAC1B,OAAyB;QAEzB,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAEM,KAAK,CAAC,eAAe,CAC1B,OAAyB;QAEzB,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,OAA8B;QAE9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE9D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,OAA8B;QAE9B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE9D,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,OAA6B;QAE7B,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAEM,KAAK,CAAC,eAAe,CAC1B,IAAqB;QAErB,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAEM,KAAK,CAAC,wBAAwB,CACnC,OAAwB;QAExB,OAAO,IAAI,CAAC,SAAS,CAAC,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAEM,KAAK,CAAC,sBAAsB,CACjC,OAA8B;QAE9B,OAAO,IAAI,CAAC,SAAS,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;CACF;AAjED,sCAiEC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rng-errors.d.ts","sourceRoot":"","sources":["../../src/errors/rng-errors.ts"],"names":[],"mappings":"AAEA,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,IAAI,EAAE,MAAM;aACZ,aAAa,CAAC,EAAE,OAAO;gBAFvC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,aAAa,CAAC,EAAE,OAAO,YAAA;CAK1C;AAED,qBAAa,kBAAmB,SAAQ,eAAe;gBAEnD,OAAO,GAAE,MAA2C,EACpD,aAAa,CAAC,EAAE,OAAO;CAK1B;AAED,qBAAa,mBAAoB,SAAQ,KAAK;aAG1B,aAAa,CAAC,EAAE,OAAO;gBADvC,OAAO,GAAE,MAA2C,EACpC,aAAa,CAAC,EAAE,OAAO,YAAA;CAK1C;AAED,qBAAa,gBAAiB,SAAQ,KAAK;aAGvB,UAAU,EAAE,MAAM;aAClB,aAAa,CAAC,EAAE,OAAO;gBAFvC,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,aAAa,CAAC,EAAE,OAAO,YAAA;CAK1C;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,KAAK,CA+CP"}
|