// priority comparator
const availability = Order.make<TRef.TRef<ProxyState>>(
(a: TRef.TRef<ProxyState>, b: TRef.TRef<ProxyState>) => {
const a = E.runSync(TRef.get(a));
const b = E.runSync(TRef.get(b));
const timeDiff = a.availableAt - b.availableAt;
return timeDiff < 0 ? 1 : timeDiff === 0 ? 0 : -1;
},
);
const tPriorityQueue: TPriorityQueue<TRef<ProxyState>> = yield* pipe(proxies,
E.map(STM.forEach(createProxy)),
E.flatMap(STM.flatMap(TPriorityQueue.fromIterable(availability))),
);
const acquireProxy = (now: number) =>
STM.gen(function* () {
const proxyRef = yield* TPriorityQueue.take(tPriorityQueue);
const state = yield* TRef.get(proxyRef);
// if resource already in usage (consumed)
if (state.availableAt > now) {
yield* TPriorityQueue.offer(tPriorityQueue, proxyRef);
yield* STM.retry;
}
// otherwise update it's state-wait-return proxy URL
const remainingUses = state.remainingUses - 1;
const cooldown = remainingUses === 0;
const availableAt = cooldown
? now + proxyConfig.cooldown.pipe(Duration.toMillis)
: now + proxyConfig.gap.pipe(Duration.toMillis);
yield* TRef.update(proxyRef, (s) => ({
...s,
remainingUses: cooldown ? proxyConfig.maxNumUsage : remainingUses,
availableAt,
}));
yield* TPriorityQueue.offer(tPriorityQueue, proxyRef);
return state.proxy;
});
const proxyResource = STM.acquireUseRelease(acquireProxy, () => STM.void);
// priority comparator
const availability = Order.make<TRef.TRef<ProxyState>>(
(a: TRef.TRef<ProxyState>, b: TRef.TRef<ProxyState>) => {
const a = E.runSync(TRef.get(a));
const b = E.runSync(TRef.get(b));
const timeDiff = a.availableAt - b.availableAt;
return timeDiff < 0 ? 1 : timeDiff === 0 ? 0 : -1;
},
);
const tPriorityQueue: TPriorityQueue<TRef<ProxyState>> = yield* pipe(proxies,
E.map(STM.forEach(createProxy)),
E.flatMap(STM.flatMap(TPriorityQueue.fromIterable(availability))),
);
const acquireProxy = (now: number) =>
STM.gen(function* () {
const proxyRef = yield* TPriorityQueue.take(tPriorityQueue);
const state = yield* TRef.get(proxyRef);
// if resource already in usage (consumed)
if (state.availableAt > now) {
yield* TPriorityQueue.offer(tPriorityQueue, proxyRef);
yield* STM.retry;
}
// otherwise update it's state-wait-return proxy URL
const remainingUses = state.remainingUses - 1;
const cooldown = remainingUses === 0;
const availableAt = cooldown
? now + proxyConfig.cooldown.pipe(Duration.toMillis)
: now + proxyConfig.gap.pipe(Duration.toMillis);
yield* TRef.update(proxyRef, (s) => ({
...s,
remainingUses: cooldown ? proxyConfig.maxNumUsage : remainingUses,
availableAt,
}));
yield* TPriorityQueue.offer(tPriorityQueue, proxyRef);
return state.proxy;
});
const proxyResource = STM.acquireUseRelease(acquireProxy, () => STM.void);