@oneuptime/common 12.0.2 → 12.0.3
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/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- package/Models/DatabaseModels/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +39 -0
- package/Server/EnvironmentConfig.ts +31 -1
- package/Server/Infrastructure/GlobalCache.ts +115 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/DatabaseService.ts +111 -0
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +26 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +226 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +267 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- package/Tests/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Decimal.ts +16 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- package/Utils/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +39 -0
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +27 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +91 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +86 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -0
- package/build/dist/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.js.map +1 -0
- package/build/dist/Types/Database/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- package/build/dist/Types/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import SingleFlight from "../../../Server/Utils/SingleFlight";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* SingleFlight collapses duplicate concurrent work inside one process.
|
|
6
|
+
*
|
|
7
|
+
* On the telemetry ingest path a single OTLP batch resolves the same service
|
|
8
|
+
* once per resource — the collector config OneUptime ships emits hundreds of
|
|
9
|
+
* resources per batch — so without coalescing, hundreds of coroutines
|
|
10
|
+
* independently ask Redis the same question and throw the answer away. The
|
|
11
|
+
* distributed fence downstream is still what guarantees correctness; this is
|
|
12
|
+
* what stops each pod from paying for that guarantee N times over.
|
|
13
|
+
*
|
|
14
|
+
* Two properties carry the weight, and both are easy to break in a refactor:
|
|
15
|
+
* the map entry must be removed when the work settles (or the process leaks a
|
|
16
|
+
* promise per key, forever), and it must be removed only if it is still the
|
|
17
|
+
* CURRENT entry (or a later leader's live run gets evicted and the next caller
|
|
18
|
+
* starts a duplicate of work already underway).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
describe("SingleFlight", () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
SingleFlight.clear();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
SingleFlight.clear();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("runs the work and returns its result", async () => {
|
|
31
|
+
const result: string = await SingleFlight.run("key", async () => {
|
|
32
|
+
return "value";
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(result).toBe("value");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("collapses concurrent callers of the same key into one run", async () => {
|
|
39
|
+
let runs: number = 0;
|
|
40
|
+
let release: (() => void) | null = null;
|
|
41
|
+
|
|
42
|
+
const gate: Promise<void> = new Promise<void>((resolve: () => void) => {
|
|
43
|
+
release = resolve;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const work: () => Promise<number> = async (): Promise<number> => {
|
|
47
|
+
runs++;
|
|
48
|
+
await gate;
|
|
49
|
+
return runs;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const all: Promise<Array<number>> = Promise.all(
|
|
53
|
+
Array.from({ length: 100 }, () => {
|
|
54
|
+
return SingleFlight.run("key", work);
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
release!();
|
|
59
|
+
const results: Array<number> = await all;
|
|
60
|
+
|
|
61
|
+
expect(runs).toBe(1);
|
|
62
|
+
expect(results).toHaveLength(100);
|
|
63
|
+
expect(new Set(results)).toEqual(new Set([1]));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("keeps different keys independent", async () => {
|
|
67
|
+
let runs: number = 0;
|
|
68
|
+
|
|
69
|
+
await Promise.all([
|
|
70
|
+
SingleFlight.run("a", async () => {
|
|
71
|
+
runs++;
|
|
72
|
+
}),
|
|
73
|
+
SingleFlight.run("b", async () => {
|
|
74
|
+
runs++;
|
|
75
|
+
}),
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
expect(runs).toBe(2);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* Coalescing applies only to work that is genuinely IN FLIGHT. Caching the
|
|
83
|
+
* result beyond that would turn a latency optimisation into a correctness
|
|
84
|
+
* hazard — the caller's next call must see the world as it is now.
|
|
85
|
+
*/
|
|
86
|
+
test("does not cache across sequential calls", async () => {
|
|
87
|
+
let runs: number = 0;
|
|
88
|
+
|
|
89
|
+
await SingleFlight.run("key", async () => {
|
|
90
|
+
runs++;
|
|
91
|
+
});
|
|
92
|
+
await SingleFlight.run("key", async () => {
|
|
93
|
+
runs++;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(runs).toBe(2);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("releases the key once the work settles", async () => {
|
|
100
|
+
await SingleFlight.run("key", async () => {
|
|
101
|
+
return "value";
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(SingleFlight.inflightCount()).toBe(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("propagates a rejection to every joined caller", async () => {
|
|
108
|
+
let release: (() => void) | null = null;
|
|
109
|
+
const gate: Promise<void> = new Promise<void>((resolve: () => void) => {
|
|
110
|
+
release = resolve;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const work: () => Promise<void> = async (): Promise<void> => {
|
|
114
|
+
await gate;
|
|
115
|
+
throw new Error("boom");
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const results: Array<Promise<void>> = Array.from({ length: 5 }, () => {
|
|
119
|
+
return SingleFlight.run("key", work);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
release!();
|
|
123
|
+
|
|
124
|
+
for (const result of results) {
|
|
125
|
+
await expect(result).rejects.toThrow("boom");
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
/*
|
|
130
|
+
* A leaked entry after a failure is the worst outcome available: the key
|
|
131
|
+
* would be permanently occupied by a settled, rejected promise and every
|
|
132
|
+
* future caller would inherit a stale error instead of retrying.
|
|
133
|
+
*/
|
|
134
|
+
test("releases the key when the work rejects", async () => {
|
|
135
|
+
await expect(
|
|
136
|
+
SingleFlight.run("key", async () => {
|
|
137
|
+
throw new Error("boom");
|
|
138
|
+
}),
|
|
139
|
+
).rejects.toThrow("boom");
|
|
140
|
+
|
|
141
|
+
expect(SingleFlight.inflightCount()).toBe(0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("recovers on the next call after a rejection", async () => {
|
|
145
|
+
await expect(
|
|
146
|
+
SingleFlight.run("key", async () => {
|
|
147
|
+
throw new Error("boom");
|
|
148
|
+
}),
|
|
149
|
+
).rejects.toThrow("boom");
|
|
150
|
+
|
|
151
|
+
await expect(
|
|
152
|
+
SingleFlight.run("key", async () => {
|
|
153
|
+
return "ok";
|
|
154
|
+
}),
|
|
155
|
+
).resolves.toBe("ok");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/*
|
|
159
|
+
* A synchronously-throwing callback must not escape past the bookkeeping —
|
|
160
|
+
* calling it bare would leave the key registered forever.
|
|
161
|
+
*/
|
|
162
|
+
test("releases the key when the work throws synchronously", async () => {
|
|
163
|
+
await expect(
|
|
164
|
+
SingleFlight.run("key", (): Promise<void> => {
|
|
165
|
+
throw new Error("sync boom");
|
|
166
|
+
}),
|
|
167
|
+
).rejects.toThrow("sync boom");
|
|
168
|
+
|
|
169
|
+
expect(SingleFlight.inflightCount()).toBe(0);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("tracks in-flight work while it is running", async () => {
|
|
173
|
+
let release: (() => void) | null = null;
|
|
174
|
+
const gate: Promise<void> = new Promise<void>((resolve: () => void) => {
|
|
175
|
+
release = resolve;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const running: Promise<void> = SingleFlight.run("key", async () => {
|
|
179
|
+
await gate;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(SingleFlight.inflightCount()).toBe(1);
|
|
183
|
+
|
|
184
|
+
release!();
|
|
185
|
+
await running;
|
|
186
|
+
|
|
187
|
+
expect(SingleFlight.inflightCount()).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
/*
|
|
191
|
+
* The `finally` must not evict an entry it does not own. Without the
|
|
192
|
+
* identity check, the first run's cleanup deletes the SECOND run's live
|
|
193
|
+
* entry, and a third caller then starts a duplicate of work already
|
|
194
|
+
* underway — precisely the stampede this class exists to prevent.
|
|
195
|
+
*/
|
|
196
|
+
test("a finished run never evicts a later run's entry", async () => {
|
|
197
|
+
await SingleFlight.run("key", async () => {
|
|
198
|
+
return 1;
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
let release: (() => void) | null = null;
|
|
202
|
+
const gate: Promise<void> = new Promise<void>((resolve: () => void) => {
|
|
203
|
+
release = resolve;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
let secondRuns: number = 0;
|
|
207
|
+
const second: Promise<void> = SingleFlight.run("key", async () => {
|
|
208
|
+
secondRuns++;
|
|
209
|
+
await gate;
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const joined: Promise<void> = SingleFlight.run("key", async () => {
|
|
213
|
+
secondRuns++;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
release!();
|
|
217
|
+
await Promise.all([second, joined]);
|
|
218
|
+
|
|
219
|
+
expect(secondRuns).toBe(1);
|
|
220
|
+
});
|
|
221
|
+
});
|