Remove broker session rest

Reverts 062ec07 and 5eb0ea7: every firm's broker session stays logged in
around the clock again, renewing its token on the fixed timer as before.
The tree is back to 6dd2528's. A session_rest row left in settings is
unused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-10-21 23:39:53 -05:00
co-authored by Claude Opus 5.5
parent 062ec07f0a
commit 5ec5abde6e
19 changed files with 43 additions and 901 deletions
+6 -38
View File
@@ -97,14 +97,6 @@ const INBOUND_SILENCE_MS = 150_000;
const STUCK_CLOSING_MS = 15_000;
const TOKEN_RENEW_CHECK_MS = 2 * 60_000;
const TOKEN_RENEW_WINDOW_MS = 15 * 60_000;
/** Each token is renewed at a point drawn between these two lengths before
* its expiry, so renewals keep no fixed rhythm (TOKEN_RENEW_WINDOW_MS is the
* latest one may wait). */
const TOKEN_RENEW_EARLIEST_MS = 45 * 60_000;
/** With the expiry unknown or untrusted, renew this long after the token
* arrived instead (drawn) — well inside Tradovate's 90-minute tokens. */
const TOKEN_BLIND_RENEW_MIN_MS = 10 * 60_000;
const TOKEN_BLIND_RENEW_MAX_MS = 25 * 60_000;
/** A failed renewal this close to expiry logs in afresh instead of waiting. */
const TOKEN_RELOGIN_WINDOW_MS = 2 * 60_000;
/** A reconnect reuses the current token only if it has at least this long left. */
@@ -434,8 +426,6 @@ export class TradovateClient {
private workingOrders: { [accountId: number]: number } = {};
private livenessInterval: ReturnType<typeof setInterval> | null = null;
private renewInFlight = false;
/** When the current token is to be renewed (drawn in noteTokenExpiry()). */
private renewAt = 0;
/** The current token's expirationTime was (nearly) past on arrival: this
* machine's clock is off, so the expiry is ignored (noteTokenExpiry()). */
private tokenExpiryUntrusted = false;
@@ -1853,26 +1843,17 @@ export class TradovateClient {
* Tradovate's, not that the token is dead. Trusting that expiry would have
* the watchdog "expire" every fresh token and log in again every 15s (a
* rate-limit penalty, then a captcha, for every client on this IP).
* Treat the expiry as unknown instead: renew 10–25 minutes after each
* token arrives (this clock's differences still hold) and log in on
* reconnect.
*
* Also draws when this token is renewed: a random 15–45 minutes before it
* expires, so the renewals don't tick like a clock.
* Treat the expiry as unknown instead: renew every 2 minutes and log in on
* reconnect, as the code did before it read the expiry at all.
*/
private noteTokenExpiry(): void {
const raw = this.accessInfo?.expirationTime;
const now = Date.now();
const t = raw ? new Date(raw).getTime() : NaN;
const untrusted = Number.isFinite(t) && t - now < TOKEN_RENEW_WINDOW_MS;
const untrusted = Number.isFinite(t) && t - Date.now() < TOKEN_RENEW_WINDOW_MS;
if (untrusted && !this.tokenExpiryUntrusted) {
console.warn(`${this.tag} new token expires ${raw}, which is (nearly) past by this machine's clock — check the system clock; ignoring the expiry`);
}
this.tokenExpiryUntrusted = untrusted;
const expiresAt = this.tokenExpiresAt();
this.renewAt = expiresAt > 0
? expiresAt - TOKEN_RENEW_WINDOW_MS - Math.random() * (TOKEN_RENEW_EARLIEST_MS - TOKEN_RENEW_WINDOW_MS)
: now + TOKEN_BLIND_RENEW_MIN_MS + Math.random() * (TOKEN_BLIND_RENEW_MAX_MS - TOKEN_BLIND_RENEW_MIN_MS);
}
/** Holding a token known to stay valid for at least `ms` more. */
@@ -1880,13 +1861,13 @@ export class TradovateClient {
return !!this.accessInfo?.accessToken && this.tokenExpiresAt() > Date.now() + ms;
}
/** The 2-minute renewal check: renews once the token's drawn renewal
* time (noteTokenExpiry) has passed. Never rejects, never overlaps itself. */
/** The 2-minute renewal check. Never rejects, never overlaps itself. */
private maybeRenewToken(): void {
if (this.intentionalDisconnect || this.connecting || this.renewInFlight || !this.accessInfo?.accessToken) return;
const now = Date.now();
const expiresAt = this.tokenExpiresAt();
if (now < this.renewAt) return;
// An unparseable expiry renews anyway — an extra renewal beats a silent expiry.
if (expiresAt > 0 && expiresAt - now > TOKEN_RENEW_WINDOW_MS) return;
// Already expired: renewal can't save it; the watchdog / next connect logs in.
if (expiresAt > 0 && expiresAt <= now) return;
@@ -2297,19 +2278,6 @@ export class TradovateClient {
for (const info of watchers) info.cancel?.('client disconnected');
this.daysFetchInFlight = false;
this.nextDaysFetchRetryAt = 0;
// Logged out for good: a snapshot left in the pool (session rest)
// must not be able to reach the broker. Blank, not removed — late
// readers use accessInfo?.accessToken and see a logged-out client.
if (this.accessInfo) this.accessInfo = { ...this.accessInfo, accessToken: '', mdAccessToken: '' };
}
/** See PlatformClient: what the session still waits on besides positions
* and working orders. */
public restBlocker(): string | null {
const pending = Object.keys(this.directEventCallbacks).length + this.indirectEventCallbacks.length;
if (pending > 0) return `${pending} request(s) awaiting an answer`;
if (this.daysFetchInFlight) return 'a report fetch is underway';
return null;
}
/** Register a one-time callback for when a fill arrives for a given orderId. */