refactor!: simplify runtime coordination #7
Backend verify / commits (pull_request) Successful in 29s
Backend verify / security (pull_request) Successful in 8s
Backend verify / verify (pull_request) Failing after 9m36s

Keep financial ownership, atomic publication and refresh CAS.
Failed refresh leaves cookies untouched; logout still clears them.

Remove full-history candle prefetch and special metadata retries.
Use native Pino request context instead of parallel middleware.

BREAKING CHANGE: remove investment stateVersion, queued sync state,
and tbank-sync status progress/resources. Consumers must observe
resources through sync=observe with the updated frontend contract.
This commit is contained in:
2026-10-03 11:38:08 +03:00
parent 0f27da9fd1
commit d698ed9425
48 changed files with 185 additions and 925 deletions
+9 -27
View File
@@ -46,10 +46,8 @@ const NO_RPC_ATTEMPTS = 0;
const NEXT_DAY_OFFSET = 1;
const RATE_WINDOW_MS = 1_000;
const METADATA_MAX_ATTEMPTS = 3;
const RPC_MAX_ATTEMPTS = 2;
const MAX_QUEUE_SIZE = 50;
const METADATA_RETRY_DELAY_MS = 2_000;
const RPC_RETRY_DELAY_MS = 1_000;
const requestDeadlineMs = 15_000;
@@ -419,12 +417,6 @@ export class TbankClient {
).then((response) => response.lastPrices ?? []);
}
getLastPrice(token: string, uid: string): Promise<null | TbankLastPrice> {
return this.getLastPrices(token, [uid]).then(
(prices) => prices.find((item) => item.instrumentUid === uid) ?? null,
);
}
getTradingStatus(token: string, uid: string): Promise<null | string> {
return this.call<{ tradingStatus?: string }>(
token,
@@ -486,8 +478,7 @@ export class TbankClient {
request: object,
): Promise<Response> {
const metadataCall = method === 'getInstrumentBy';
const maxAttempts = metadataCall ? METADATA_MAX_ATTEMPTS : RPC_MAX_ATTEMPTS;
for (let attempts = FIRST_ATTEMPT; attempts <= maxAttempts; attempts += ATTEMPT_INCREMENT) {
for (let attempts = FIRST_ATTEMPT; attempts <= RPC_MAX_ATTEMPTS; attempts += ATTEMPT_INCREMENT) {
const startedAt = performance.now();
const queueFull = this.queue.size >= MAX_QUEUE_SIZE;
try {
@@ -508,34 +499,25 @@ export class TbankClient {
const failureContext = queueFull
? { code: TbankErrorCode.Unavailable }
: { grpcCode };
const retryable = queueFull
? metadataCall
: [
status.DEADLINE_EXCEEDED,
status.RESOURCE_EXHAUSTED,
status.UNAVAILABLE,
].includes(grpcCode);
if (retryable && attempts < maxAttempts) {
const retryable = !queueFull && [
status.DEADLINE_EXCEEDED,
status.RESOURCE_EXHAUSTED,
status.UNAVAILABLE,
].includes(grpcCode);
if (retryable && attempts < RPC_MAX_ATTEMPTS) {
this.logger.event('warn', 'tbank.rpc.retry', {
operation: method,
...failureContext,
attempts,
durationMs: Math.round(performance.now() - startedAt),
});
await new Promise((resolve) =>
setTimeout(
resolve,
metadataCall
? attempts * METADATA_RETRY_DELAY_MS
: RPC_RETRY_DELAY_MS,
),
);
await new Promise((resolve) => setTimeout(resolve, RPC_RETRY_DELAY_MS));
continue;
}
this.logger.event('error', 'tbank.rpc.failed', {
operation: method,
...failureContext,
attempts: queueFull && !metadataCall ? NO_RPC_ATTEMPTS : attempts,
attempts: queueFull ? NO_RPC_ATTEMPTS : attempts,
durationMs: Math.round(performance.now() - startedAt),
});
if (