mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-12 03:56:15 -08:00
feat(stalker): install discovered principals atomically
This commit is contained in:
1 parent
79729c4dfc
commit
97c50071ac
2 files changed
+60
-36
No files matched your search
+29
-32
@@ -107,14 +107,11 @@ describe('StalkerBaseIdentityCoordinator', () => {
|
||||
}
|
||||
);
|
||||
await Promise.resolve();
|
||||
const mutation = coordinator.runMutation(
|
||||
'principal-a',
|
||||
async () => {
|
||||
order.push('mutation-start');
|
||||
await mutationGate.promise;
|
||||
order.push('mutation-end');
|
||||
}
|
||||
);
|
||||
const mutation = coordinator.runMutation('principal-a', async () => {
|
||||
order.push('mutation-start');
|
||||
await mutationGate.promise;
|
||||
order.push('mutation-end');
|
||||
});
|
||||
const laterReader = coordinator.runRead(
|
||||
'principal-a',
|
||||
ready.snapshot.epoch + 1,
|
||||
@@ -128,11 +125,7 @@ describe('StalkerBaseIdentityCoordinator', () => {
|
||||
readerGate.resolve();
|
||||
await reader;
|
||||
await Promise.resolve();
|
||||
expect(order).toEqual([
|
||||
'reader-start',
|
||||
'reader-end',
|
||||
'mutation-start',
|
||||
]);
|
||||
expect(order).toEqual(['reader-start', 'reader-end', 'mutation-start']);
|
||||
mutationGate.resolve();
|
||||
await mutation;
|
||||
await laterReader;
|
||||
@@ -158,21 +151,13 @@ describe('StalkerBaseIdentityCoordinator', () => {
|
||||
const operation = jest.fn();
|
||||
|
||||
await expect(
|
||||
coordinator.runRead(
|
||||
'principal-a',
|
||||
first.snapshot.epoch,
|
||||
operation
|
||||
)
|
||||
coordinator.runRead('principal-a', first.snapshot.epoch, operation)
|
||||
).resolves.toEqual({
|
||||
kind: 'suspended',
|
||||
snapshot: { activePrincipal: 'principal-b', epoch: 2 },
|
||||
});
|
||||
await expect(
|
||||
coordinator.runRead(
|
||||
'principal-b',
|
||||
first.snapshot.epoch,
|
||||
operation
|
||||
)
|
||||
coordinator.runRead('principal-b', first.snapshot.epoch, operation)
|
||||
).resolves.toEqual({
|
||||
kind: 'suspended',
|
||||
snapshot: { activePrincipal: 'principal-b', epoch: 2 },
|
||||
@@ -228,11 +213,7 @@ describe('StalkerBaseIdentityCoordinator', () => {
|
||||
|
||||
expect(coordinator.snapshot).toEqual({ epoch: 2 });
|
||||
await expect(
|
||||
coordinator.runRead(
|
||||
'principal-a',
|
||||
ready.snapshot.epoch,
|
||||
jest.fn()
|
||||
)
|
||||
coordinator.runRead('principal-a', ready.snapshot.epoch, jest.fn())
|
||||
).resolves.toEqual({
|
||||
kind: 'suspended',
|
||||
snapshot: { epoch: 2 },
|
||||
@@ -241,10 +222,7 @@ describe('StalkerBaseIdentityCoordinator', () => {
|
||||
|
||||
it('can explicitly enter a no-active-principal transition epoch', async () => {
|
||||
const coordinator = new StalkerBaseIdentityCoordinator();
|
||||
await coordinator.runMutation(
|
||||
'principal-a',
|
||||
async () => undefined
|
||||
);
|
||||
await coordinator.runMutation('principal-a', async () => undefined);
|
||||
|
||||
const transition = await coordinator.runMutation(
|
||||
undefined,
|
||||
@@ -256,4 +234,23 @@ describe('StalkerBaseIdentityCoordinator', () => {
|
||||
value: 'transition',
|
||||
});
|
||||
});
|
||||
|
||||
it('installs a principal discovered inside one exclusive mutation without releasing the gate', async () => {
|
||||
const coordinator = new StalkerBaseIdentityCoordinator();
|
||||
|
||||
const result = await coordinator.runDiscoveredPrincipalMutation(
|
||||
async (epoch) => ({
|
||||
principal: 'discovered-principal',
|
||||
value: `authenticated-${epoch}`,
|
||||
})
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
snapshot: {
|
||||
activePrincipal: 'discovered-principal',
|
||||
epoch: 1,
|
||||
},
|
||||
value: 'authenticated-1',
|
||||
});
|
||||
});
|
||||
});
|
||||
+31
-4
@@ -21,6 +21,11 @@ export interface StalkerCoordinatedMutationResult<T> {
|
||||
readonly value: T;
|
||||
}
|
||||
|
||||
export interface StalkerDiscoveredPrincipalMutationValue<T> {
|
||||
readonly principal: string;
|
||||
readonly value: T;
|
||||
}
|
||||
|
||||
interface ReadWaiter {
|
||||
readonly kind: 'read';
|
||||
readonly resolve: () => void;
|
||||
@@ -107,6 +112,31 @@ export class StalkerBaseIdentityCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
async runDiscoveredPrincipalMutation<T>(
|
||||
operation: (
|
||||
mutationEpoch: number
|
||||
) => Promise<StalkerDiscoveredPrincipalMutationValue<T>>
|
||||
): Promise<StalkerCoordinatedMutationResult<T>> {
|
||||
await this.#acquireWrite();
|
||||
try {
|
||||
if (this.#epoch >= Number.MAX_SAFE_INTEGER) {
|
||||
throw new Error('stalker-coordinator-epoch-exhausted');
|
||||
}
|
||||
this.#epoch += 1;
|
||||
this.#activePrincipal = undefined;
|
||||
|
||||
const result = await operation(this.#epoch);
|
||||
validatePrincipal(result.principal);
|
||||
this.#activePrincipal = result.principal;
|
||||
return {
|
||||
snapshot: this.snapshot,
|
||||
value: result.value,
|
||||
};
|
||||
} finally {
|
||||
this.#releaseWrite();
|
||||
}
|
||||
}
|
||||
|
||||
async #acquireRead(): Promise<void> {
|
||||
if (!this.#writerActive && this.#waiters.length === 0) {
|
||||
this.#activeReaders += 1;
|
||||
@@ -178,10 +208,7 @@ export class StalkerBaseIdentityCoordinator {
|
||||
* single server-side token for the whole origin/MAC pair.
|
||||
*/
|
||||
export class StalkerBaseIdentityCoordinatorPool {
|
||||
readonly #coordinators = new Map<
|
||||
string,
|
||||
StalkerBaseIdentityCoordinator
|
||||
>();
|
||||
readonly #coordinators = new Map<string, StalkerBaseIdentityCoordinator>();
|
||||
|
||||
get size(): number {
|
||||
return this.#coordinators.size;
|
||||
|
||||
Reference in new issue
Block a user