@mentra/engine 3.4.0-dev.1207 → 3.4.0-dev.1209
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/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +5 -3
- package/build/facades/glasses.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/AppRegistry.d.ts +2 -0
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +2 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +18 -1
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/MentraJSCrashController.d.ts +2 -0
- package/build/services/MentraJSCrashController.d.ts.map +1 -1
- package/build/services/MentraJSCrashController.js +4 -0
- package/build/services/MentraJSCrashController.js.map +1 -1
- package/build/services/MentraJSRouter.d.ts.map +1 -1
- package/build/services/MentraJSRouter.js +14 -3
- package/build/services/MentraJSRouter.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts +6 -0
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +20 -3
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/fleet/FleetReporter.d.ts.map +1 -1
- package/build/services/fleet/FleetReporter.js +43 -0
- package/build/services/fleet/FleetReporter.js.map +1 -1
- package/build/services/fleet/fleetCollector.d.ts +1 -1
- package/build/services/fleet/fleetCollector.d.ts.map +1 -1
- package/build/services/fleet/fleetCollector.js +146 -5
- package/build/services/fleet/fleetCollector.js.map +1 -1
- package/build/services/fleet/fleetEvents.d.ts +39 -3
- package/build/services/fleet/fleetEvents.d.ts.map +1 -1
- package/build/services/fleet/fleetEvents.js +66 -22
- package/build/services/fleet/fleetEvents.js.map +1 -1
- package/build/services/fleet/fleetQueue.d.ts +26 -0
- package/build/services/fleet/fleetQueue.d.ts.map +1 -1
- package/build/services/fleet/fleetQueue.js +94 -8
- package/build/services/fleet/fleetQueue.js.map +1 -1
- package/build/services/fleet/telemetryLimits.d.ts +16 -0
- package/build/services/fleet/telemetryLimits.d.ts.map +1 -0
- package/build/services/fleet/telemetryLimits.js +69 -0
- package/build/services/fleet/telemetryLimits.js.map +1 -0
- package/build/services/installMiniapp.d.ts +13 -0
- package/build/services/installMiniapp.d.ts.map +1 -1
- package/build/services/installMiniapp.js +64 -1
- package/build/services/installMiniapp.js.map +1 -1
- package/build/services/miniappReleaseInstall.js +1 -1
- package/build/services/miniappReleaseInstall.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +16 -1
- package/build/stores/apps.js.map +1 -1
- package/package.json +8 -8
- package/src/facades/glasses.ts +5 -3
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/services/AppRegistry.ts +2 -0
- package/src/services/LocalMiniappRuntime.ts +20 -1
- package/src/services/MentraJSCrashController.ts +5 -0
- package/src/services/MentraJSRouter.ts +15 -3
- package/src/services/MiniappLauncher.ts +18 -2
- package/src/services/fleet/FleetReporter.ts +40 -0
- package/src/services/fleet/fleetCollector.ts +160 -6
- package/src/services/fleet/fleetEvents.ts +104 -20
- package/src/services/fleet/fleetQueue.ts +100 -7
- package/src/services/fleet/telemetryLimits.ts +78 -0
- package/src/services/installMiniapp.ts +78 -2
- package/src/services/miniappReleaseInstall.ts +1 -1
- package/src/stores/apps.ts +19 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FleetReporter.js","sourceRoot":"","sources":["../../../src/services/fleet/FleetReporter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Fleet reporter: runs the collector for the signed-in account and delivers its records to the phone's own Core.\n *\n * A run is one account on one Core, which is one partition of the durable queue. Each run starts the collector with\n * a new collector session and uploads only its own partition, as that account, to that Core: every request goes\n * through `reportAs`, which sends nothing unless the client still holds that account's token for that Core. A\n * sign-out, a sign-in or a client rebuilt for another Core ends the run (the collector writes its final checkpoints\n * into the old partition), and the next run starts once the client says whose token it holds. When it cannot say yet\n * (offline, Core unavailable) the account is not known, which is not a sign-out: the reporter asks again after the\n * same backoff as uploads, until it is answered, a sign-out comes, or the engine stops. An old partition's records\n * wait in the queue until their account signs in to their Core again, or expire into a coverage gap; they are never\n * sent as anyone else.\n *\n * A miniapp launch the engine makes while no run is collecting (it restores miniapps while the account may still be\n * being resolved) is kept by `fleetEvents` for the next run, which is that account's. A sign-in or sign-out discards\n * it: what happened before belongs to no one the next run collects for. So do the engine's start and stop.\n *\n * Uploads go a minute after records arrive, or at once when 100 are waiting, at most 500 records (and 1 MB of them)\n * per request. With nothing waiting the uploader sets no timer: the next record wakes it. What each answer does:\n * - accepted or duplicate: the record is removed. Rejected: removed, with a `record_rejected` gap over its\n * observation time. Retryable: kept, and the next request waits at least its Retry-After.\n * - not installed: collection stops and the partition is dropped, until the next sign-in or engine start.\n * - no Fleet route (a 404 without Fleet's code: a Core from before Fleet, or a proxy): collection stops until the next\n * sign-in or engine start, as for not installed, but the partition is kept; its records expire after 7 days.\n * - unavailable, no answer within a minute, an authentication failure, or anything unexpected: everything is kept,\n * and the next attempt waits min(30 min, 30 s * 2^n) +-20%, or Retry-After when given (at most 30 minutes). Only\n * an answer that settles a record (accepted, duplicate or rejected) resets n.\n * - forbidden: everything is kept, and the next attempt waits 30 minutes.\n * - too large, or refused as a batch: the batch is halved; a single record still refused is removed with a gap. The\n * batch does not grow back to a refused size until 10 sends have succeeded below it.\n *\n * After any failure the next batch is a probe of 5 records, and each success doubles the batch again, up to 500. A\n * batch of a single record is never sent straight after another request: it waits the normal minute.\n *\n * Before each upload, the records the queue dropped or expired become one coverage gap, queued in the same tick.\n *\n * The reporter also carries two things between Fleet and the reconciler (`fleetRevisionSignal`):\n * - an ack that names a desired-state `revision` is passed on with `signalFleetRevision`, whatever else the ack says\n * (an ack without one signals nothing; the reconciler ignores a revision it already applied);\n * - each assignment outcome the reconciler emits becomes an `assignment_outcome` record in the current run's\n * partition, once it is a valid payload. The outcome carries no account, so, like a launch, one heard while no run\n * is collecting is kept (the latest per assignment) for the next run, and a sign-in, sign-out or the engine's start\n * and stop discards it: it is never sent as anyone but the account running when it was heard.\n *\n * Fleet is decided by the phone's Core alone. The reporter reads no deployment or telemetry setting, and nothing it\n * does can block the engine: every entry point catches its own failures.\n */\nimport {\n FLEET_LIMITS,\n FLEET_SCHEMA_VERSION,\n FleetAssignmentOutcomePayloadSchema,\n type FleetAssignmentOutcomePayload,\n type FleetRecord,\n} from \"@mentra/cloud-protocol\"\nimport {AppState, Platform} from \"react-native\"\n\nimport {BgTimer} from \"../../utils/timers\"\nimport {\n cloudClientService,\n type CoreSession,\n type CoreSessionChange,\n type FleetSendOutcome,\n} from \"../CloudClientService\"\nimport {createFleetCollector, type FleetCollector} from \"./fleetCollector\"\nimport {fleetEvents} from \"./fleetEvents\"\nimport {newId, phoneDeviceId} from \"./fleetIds\"\nimport {createFleetQueue, partitionKey, type FleetLosses, type FleetQueue} from \"./fleetQueue\"\nimport {onAssignmentOutcome, signalFleetRevision} from \"./fleetRevisionSignal\"\n\nexport interface FleetReporter {\n /** Sweeps the queue, starts collecting for the signed-in account (at once when it is known) and starts uploading. */\n start(): void\n /** Ends collection (`engine_stopped`), abandons a request in flight and drops every timer and listener. */\n stop(): Promise<void>\n}\n\ntype CloudClient = typeof cloudClientService\n/** What the reporter uses of the engine's Core client. */\nexport type FleetReporterCloud = Pick<CloudClient, \"getCoreSession\" | \"resolveCoreSession\" | \"onCoreSessionChanged\"> & {\n core: {fleet: Pick<CloudClient[\"core\"][\"fleet\"], \"reportAs\">}\n}\n\nexport interface FleetReporterDeps {\n /** Defaults to the engine's Core client. */\n cloud?: FleetReporterCloud\n /** Defaults to the app's shared queue. */\n queue?: FleetQueue\n /** Builds the collector over the queue the reporter hands it. Defaults to the engine's collector. */\n createCollector?: (queue: FleetQueue) => FleetCollector\n now?: () => number\n random?: () => number\n}\n\nconst UPLOAD_INTERVAL_MS = 60_000\n/** This many records waiting are sent at once rather than at the next minute. */\nconst UPLOAD_NOW_RECORDS = 100\nconst MAX_BATCH_RECORDS = FLEET_LIMITS.maxRecordsPerBatch\n/** After a failure the next batch is this small, and doubles with each success. */\nconst PROBE_BATCH_RECORDS = 5\n/** A batch size Core refused is not tried again until this many sends have succeeded below it. */\nconst BATCH_CAP_RESET_SENDS = 10\n/** The records' own JSON; the rest of the 1 MiB request limit is headroom for the envelope and the commas. */\nconst MAX_BATCH_BYTES = 1_000_000\nconst BACKOFF_BASE_MS = 30_000\nconst BACKOFF_MAX_MS = 30 * 60_000\n/** A Retry-After of 0 still waits this long, so a server that keeps answering 0 cannot make the loop spin. */\nconst MIN_RETRY_MS = 1_000\n/** `sweepExpired` scans the whole store, so it runs at start and then at most this often. */\nconst SWEEP_INTERVAL_MS = 60 * 60_000\n/** A request still unanswered after this long is abandoned, and counts as no answer. */\nconst REQUEST_TIMEOUT_MS = 60_000\n/** Outcomes heard while no run is collecting: one per assignment, so more than this are not one boot's worth. */\nconst MAX_HELD_OUTCOMES = 64\nconst NO_ANSWER: FleetSendOutcome = {kind: \"unavailable\", retryAfterSeconds: null}\n\n/** min(30 min, 30 s * 2^failures), times a jitter between 0.8 and 1.2. */\nexport function backoffDelayMs(failures: number, random: () => number): number {\n const base = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * 2 ** Math.max(0, failures))\n return Math.round(base * (0.8 + 0.4 * random()))\n}\n\n/** A server's Retry-After, as a delay the loop can live with. */\nfunction retryDelayMs(seconds: number): number {\n return Math.min(BACKOFF_MAX_MS, Math.max(MIN_RETRY_MS, seconds * 1_000))\n}\n\nfunction iso(wall: number): string {\n return new Date(wall).toISOString()\n}\n\nfunction timeOf(value: string | null): number | null {\n if (value === null) return null\n const ms = Date.parse(value)\n return Number.isNaN(ms) ? null : ms\n}\n\nfunction warn(what: string, error: unknown): void {\n // Only the failure's message: never a record, an account or a token.\n console.warn(`FleetReporter: ${what} failed:`, error instanceof Error ? error.message : \"unknown error\")\n}\n\ntype GapPayload = Extract<FleetRecord, {kind: \"coverage_gap\"}>[\"payload\"]\n/** Fleet's answer for one record, as the Core client read it. */\ntype AckResult = Extract<FleetSendOutcome, {kind: \"results\"}>[\"ack\"][\"results\"][number]\n\n/** One account on one Core, from the start of its collection to the end. */\ninterface Run {\n /** Who the run sends as. The Core URL follows the client's spelling of the same Core. */\n session: {coreUrl: string; mentraUserId: string}\n readonly partition: string\n readonly collectorSessionId: string\n /** Aborted when the run ends, which abandons its request in flight. */\n readonly controller: AbortController\n ended: boolean\n timer: number | null\n /** When the next attempt is due (`now()` time). */\n dueAt: number\n /** The next attempt waits out a backoff the server or a failure asked for. */\n backingOff: boolean\n uploading: boolean\n /** Failed attempts since the last answer that settled a record: the n of the backoff. */\n failures: number\n batchLimit: number\n /** Below the batch sizes Core refused lately: the limit does not grow back to a refused size for a while. */\n batchCap: number\n /** Sends that succeeded since Core last refused a batch; at BATCH_CAP_RESET_SENDS the cap is lifted. */\n sendsSinceRefusal: number\n batchRefusalLogged: boolean\n}\n\n/** When to try next, and whether that wait is a backoff. */\ninterface Next {\n delayMs: number\n backingOff: boolean\n}\nconst AFTER_INTERVAL: Next = {delayMs: UPLOAD_INTERVAL_MS, backingOff: false}\nconst AT_ONCE: Next = {delayMs: 0, backingOff: false}\n/** Nothing is waiting: the uploader sets no timer until a record arrives. */\nconst ASLEEP = null\n\nexport function createFleetReporter(deps: FleetReporterDeps = {}): FleetReporter {\n const cloud = deps.cloud ?? cloudClientService\n const queue = deps.queue ?? createFleetQueue()\n const now = deps.now ?? Date.now\n const random = deps.random ?? Math.random\n\n let started = false\n let run: Run | null = null\n /** The partition its Core said has no Fleet: nothing is collected for it until a sign-in or the next start. */\n let notInstalled: string | null = null\n /** Bumped by every change of session, so only the latest resolution of the account is applied. */\n let resolution = 0\n /** Asks again who the client speaks for, after a resolution that could not tell (offline, Core unavailable). */\n let resolveRetry: number | null = null\n /** Resolutions in a row that could not tell: the n of their backoff. */\n let resolveFailures = 0\n let lastSweepAt = 0\n /** Outcomes heard while no run was collecting, the latest per assignment, for the next run (see the header). */\n const heldOutcomes = new Map<string, FleetAssignmentOutcomePayload>()\n const cleanups: Array<() => void> = []\n\n // The collector writes through this view of the queue, which wakes the uploader once 100 records wait. It\n // delegates at call time, so the shared queue stays the only queue over the store.\n const collectorQueue: FleetQueue = {\n enqueue(partition, record) {\n queue.enqueue(partition, record)\n try {\n onEnqueued(partition)\n } catch (error) {\n warn(\"waking the uploader\", error)\n }\n },\n nextBatch: (partition, maxRecords, maxBytes) => queue.nextBatch(partition, maxRecords, maxBytes),\n remove: (partition, recordIds) => queue.remove(partition, recordIds),\n takeLosses: (partition, at) => queue.takeLosses(partition, at),\n sweepExpired: (at) => queue.sweepExpired(at),\n clearPartition: (partition) => queue.clearPartition(partition),\n size: (partition) => queue.size(partition),\n nextSequence: (partition, collectorSessionId) => queue.nextSequence(partition, collectorSessionId),\n }\n const collector = (deps.createCollector ?? ((q: FleetQueue) => createFleetCollector({queue: q})))(collectorQueue)\n\n function guard(what: string, work: () => void): void {\n try {\n work()\n } catch (error) {\n warn(what, error)\n }\n }\n\n // --- runs ----------------------------------------------------------------------------------------------------\n\n function beginRun(session: Run[\"session\"], partition: string): void {\n const r: Run = {\n session,\n partition,\n collectorSessionId: newId(),\n controller: new AbortController(),\n ended: false,\n timer: null,\n dueAt: 0,\n backingOff: false,\n uploading: false,\n failures: 0,\n batchLimit: MAX_BATCH_RECORDS,\n batchCap: MAX_BATCH_RECORDS,\n sendsSinceRefusal: 0,\n batchRefusalLogged: false,\n }\n run = r\n collector.start({partition, collectorSessionId: r.collectorSessionId})\n collector.flushCheckpoint()\n for (const outcome of [...heldOutcomes.values()]) recordOutcome(r, outcome)\n heldOutcomes.clear()\n schedule(r, afterSend(r))\n }\n\n function endRun(reason: \"engine_stopped\" | \"account_changed\" | \"not_installed\"): void {\n const r = run\n if (!r) return\n run = null\n r.ended = true\n if (r.timer !== null) BgTimer.clearTimeout(r.timer)\n r.timer = null\n r.controller.abort()\n // The collector writes its final checkpoints into the run's partition.\n collector.stop(reason)\n }\n\n /**\n * Core said Fleet is not there for this session: nothing more is collected for or sent from the partition until the\n * next sign-in or engine start. Only Fleet's own \"not installed\" drops what was queued; a Core without the route\n * (or a proxy answering for it) proves nothing about Fleet, so its records wait in the queue, and expire there.\n */\n function stopForSession(r: Run, drop: boolean): void {\n notInstalled = r.partition\n endRun(\"not_installed\")\n if (drop) queue.clearPartition(r.partition)\n }\n\n /** Collects for `session` if it names an account; otherwise collects for no one. */\n function applySession(session: CoreSession | null): void {\n if (!started) return\n const user = session?.mentraUserId\n if (!session || !user) {\n endRun(\"account_changed\")\n return\n }\n const partition = partitionKey(session.coreUrl, user)\n if (run?.partition === partition) {\n // The same Core, maybe spelled differently by a rebuilt client: send as the client spells it now.\n if (run.session.coreUrl !== session.coreUrl) run.session = {coreUrl: session.coreUrl, mentraUserId: user}\n return\n }\n endRun(\"account_changed\")\n if (partition === notInstalled) return\n beginRun({coreUrl: session.coreUrl, mentraUserId: user}, partition)\n }\n\n /**\n * Asks the client who it speaks for now (refreshing its token if needed) and follows. The latest ask wins. An ask\n * the client cannot answer yet (offline, Core unavailable) is no sign-out: what is running keeps running, and the\n * ask is made again after a backoff, until it is answered or a session change asks anew.\n */\n async function resolveAndApply(): Promise<void> {\n const mine = ++resolution\n cancelResolveRetry()\n let session: CoreSession | null\n try {\n session = await cloud.resolveCoreSession()\n } catch (error) {\n if (mine !== resolution || !started) return\n warn(\"resolving the account\", error)\n retryResolution()\n return\n }\n if (mine !== resolution || !started) return\n resolveFailures = 0\n guard(\"following the account\", () => applySession(session))\n }\n\n function retryResolution(): void {\n const delayMs = backoffDelayMs(resolveFailures, random)\n resolveFailures += 1\n resolveRetry = BgTimer.setTimeout(() => {\n resolveRetry = null\n void resolveAndApply()\n }, delayMs)\n }\n\n function cancelResolveRetry(): void {\n if (resolveRetry !== null) BgTimer.clearTimeout(resolveRetry)\n resolveRetry = null\n }\n\n function onSessionChanged(change: CoreSessionChange): void {\n if (!started) return\n if (change.reason !== \"reconnect\") {\n fleetEvents.discardUndeliveredLaunches()\n heldOutcomes.clear()\n }\n if (change.reason === \"signed_out\") {\n // A resolution still under way, or one waiting to ask again, must not bring the account back.\n resolution += 1\n cancelResolveRetry()\n resolveFailures = 0\n endRun(\"account_changed\")\n return\n }\n // A new authenticated session asks Core again, including a Core that said it has no Fleet.\n if (change.reason === \"signed_in\") notInstalled = null\n // What the change already names is enough to stop collecting for an account or a Core that is no longer current.\n // A sign-in that names no account yet may be anyone: what follows is not the previous account's until the client\n // says whose token it holds. A rebuilt client that names none yet is still the session that was running.\n const r = run\n if (r) {\n const account = change.mentraUserId ?? (change.reason === \"signed_in\" ? null : r.session.mentraUserId)\n let partition: string | null = null\n try {\n partition = change.coreUrl && account ? partitionKey(change.coreUrl, account) : null\n } catch {\n partition = null\n }\n if (partition !== r.partition) endRun(\"account_changed\")\n }\n void resolveAndApply()\n }\n\n // --- the upload loop -----------------------------------------------------------------------------------------\n\n function schedule(r: Run, next: Next | typeof ASLEEP): void {\n if (r.ended) return\n if (r.timer !== null) BgTimer.clearTimeout(r.timer)\n r.timer = null\n if (next === ASLEEP) {\n r.backingOff = false\n return\n }\n r.backingOff = next.backingOff\n r.dueAt = now() + next.delayMs\n r.timer = BgTimer.setTimeout(() => {\n r.timer = null\n guard(\"upload tick\", () => tick(r))\n }, next.delayMs)\n }\n\n /**\n * A record arrived. An uploader that was asleep is woken (a minute from now, or at once when 100 wait); one that is\n * due anyway is hurried once 100 wait. Deferred to a timer either way: this runs inside whatever the collector is\n * reacting to. While a request is out or the loop is backing off, the next attempt picks the record up.\n */\n function onEnqueued(partition: string): void {\n const r = run\n if (!r || r.partition !== partition || r.uploading || r.backingOff) return\n if (r.timer === null) schedule(r, afterSend(r))\n else if (r.dueAt > now() && queue.size(partition) >= UPLOAD_NOW_RECORDS && r.batchLimit > 1) schedule(r, AT_ONCE)\n }\n\n function sweepIfDue(at: number): void {\n // A clock set back sweeps again rather than waiting for it to catch up.\n if (at >= lastSweepAt && at - lastSweepAt < SWEEP_INTERVAL_MS) return\n lastSweepAt = at\n queue.sweepExpired(at)\n }\n\n function tick(r: Run): void {\n if (r !== run || r.ended) return\n r.backingOff = false\n guard(\"sweeping the queue\", () => sweepIfDue(now()))\n if (!r.uploading) void upload(r)\n }\n\n async function upload(r: Run): Promise<void> {\n r.uploading = true\n let next: Next | typeof ASLEEP\n try {\n next = await attempt(r)\n } catch (error) {\n // Whatever went wrong, the loop goes on: back off and try again.\n warn(\"upload\", error)\n next = failed(r, null)\n } finally {\n r.uploading = false\n }\n if (r === run) schedule(r, next)\n }\n\n async function attempt(r: Run): Promise<Next | typeof ASLEEP> {\n const at = now()\n // takeLosses clears the counts it returns, so their gap is queued before anything else can happen.\n recordLosses(r, queue.takeLosses(r.partition, at), at)\n const records = queue.nextBatch(r.partition, r.batchLimit, MAX_BATCH_BYTES)\n if (records.length === 0) return ASLEEP\n let outcome: FleetSendOutcome\n try {\n outcome = await send(r, records)\n } catch (error) {\n if (r.ended) return ASLEEP\n throw error\n }\n // An answer for a run that is over changes nothing: its records are sent again as their own account later.\n if (r.ended) return ASLEEP\n return answered(r, records, outcome)\n }\n\n /**\n * Sends one batch as the run's account. The request is abandoned when the run ends or when it has gone a minute\n * without an answer, so a request that hangs cannot keep the run from ever trying again; an abandoned request is\n * no answer (`unavailable`), and whatever it says later is ignored.\n */\n async function send(r: Run, records: FleetRecord[]): Promise<FleetSendOutcome> {\n const request = new AbortController()\n let timer: number | null = null\n let stopWaiting: () => void = () => {}\n const abandoned = new Promise<FleetSendOutcome>((resolve) => {\n stopWaiting = () => resolve(NO_ANSWER)\n })\n // Lets go of the timer and the run's signal at once, so a run that ends leaves nothing of the request behind.\n const release = () => {\n if (timer !== null) BgTimer.clearTimeout(timer)\n timer = null\n r.controller.signal.removeEventListener(\"abort\", abandon)\n }\n const abandon = () => {\n release()\n request.abort()\n stopWaiting()\n }\n r.controller.signal.addEventListener(\"abort\", abandon)\n timer = BgTimer.setTimeout(() => {\n timer = null\n abandon()\n }, REQUEST_TIMEOUT_MS)\n try {\n if (r.controller.signal.aborted) abandon()\n const answer = cloud.core.fleet.reportAs(\n r.session,\n {schemaVersion: FLEET_SCHEMA_VERSION, records},\n {signal: request.signal},\n )\n // Once abandoned, how the request ends is of no interest, and must not surface as an unhandled rejection.\n answer.catch(() => {})\n return await Promise.race([answer, abandoned])\n } finally {\n release()\n }\n }\n\n function answered(r: Run, records: FleetRecord[], outcome: FleetSendOutcome): Next | typeof ASLEEP {\n switch (outcome.kind) {\n case \"results\":\n guard(\"passing on the ack revision\", () => passOnRevision(outcome.ack))\n return settle(r, records, outcome.ack.results)\n case \"not_installed\":\n stopForSession(r, true)\n return ASLEEP\n case \"no_fleet_route\":\n stopForSession(r, false)\n return ASLEEP\n case \"unavailable\":\n return failed(r, outcome.retryAfterSeconds)\n case \"unauthorized\":\n // The client refreshed the token once; records are never dropped for credentials.\n return failed(r, null)\n case \"forbidden\":\n // Core will not take this account's reports now; that changes slowly, so wait the longest backoff.\n r.batchLimit = Math.min(r.batchLimit, PROBE_BATCH_RECORDS)\n r.failures += 1\n return {delayMs: Math.round(BACKOFF_MAX_MS * (0.8 + 0.4 * random())), backingOff: true}\n case \"batch_too_large\":\n return shrink(r, records)\n case \"batch_invalid\":\n if (!r.batchRefusalLogged) {\n r.batchRefusalLogged = true\n console.warn(`FleetReporter: Core refused a batch (${outcome.code}); sending smaller batches`)\n }\n return shrink(r, records)\n case \"session_changed\":\n // The client speaks for someone else now. Nothing was sent; follow it.\n void resolveAndApply()\n return failed(r, null)\n }\n }\n\n function failed(r: Run, retryAfterSeconds: number | null): Next {\n // Whatever went wrong may still be wrong: the next attempt sends a small batch, not the whole backlog again.\n r.batchLimit = Math.min(r.batchLimit, PROBE_BATCH_RECORDS)\n const delayMs =\n retryAfterSeconds != null && Number.isFinite(retryAfterSeconds)\n ? retryDelayMs(retryAfterSeconds)\n : backoffDelayMs(r.failures, random)\n r.failures += 1\n return {delayMs, backingOff: true}\n }\n\n function settle(r: Run, records: FleetRecord[], results: AckResult[]): Next | typeof ASLEEP {\n const removed: string[] = []\n const rejected: FleetRecord[] = []\n const reasons = new Map<string, number>()\n const answeredIndexes = new Set<number>()\n let retryable = false\n let retryAfterSeconds: number | null = null\n for (const result of results) {\n const record = records[result.index]\n // A result counts only for the record at its index, and only if it names that record.\n if (!record || answeredIndexes.has(result.index)) continue\n if (result.recordId !== undefined && result.recordId !== record.recordId) continue\n answeredIndexes.add(result.index)\n if (result.status === \"accepted\" || result.status === \"duplicate\") {\n removed.push(record.recordId)\n } else if (result.status === \"rejected\") {\n removed.push(record.recordId)\n rejected.push(record)\n const reason = result.reasonCode ?? \"unknown\"\n reasons.set(reason, (reasons.get(reason) ?? 0) + 1)\n } else {\n retryable = true\n if (result.retryAfterSeconds !== undefined) {\n retryAfterSeconds = Math.max(retryAfterSeconds ?? 0, result.retryAfterSeconds)\n }\n }\n }\n // An answer that settles nothing is no answer, and one that only says \"retry\" is a failure to deliver: neither\n // resets the backoff, or a Core answering retryable to everything would get a full batch every 30 s forever.\n if (removed.length === 0) return failed(r, retryable ? retryAfterSeconds : null)\n\n r.failures = 0\n r.sendsSinceRefusal += 1\n if (r.sendsSinceRefusal >= BATCH_CAP_RESET_SENDS) r.batchCap = MAX_BATCH_RECORDS\n r.batchLimit = Math.min(r.batchCap, r.batchLimit * 2)\n const at = now()\n queue.remove(r.partition, removed)\n for (const record of rejected) recordRejected(r, record, at)\n if (rejected.length > 0) {\n const summary = [...reasons].map(([reason, count]) => `${reason} x${count}`).join(\", \")\n console.warn(`FleetReporter: Core rejected ${rejected.length} record(s): ${summary}`)\n }\n if (retryable) {\n return {\n delayMs: retryAfterSeconds !== null ? retryDelayMs(retryAfterSeconds) : backoffDelayMs(0, random),\n backingOff: true,\n }\n }\n return afterSend(r)\n }\n\n /**\n * When to send next, by what waits: nothing, and the uploader sleeps until a record arrives; 100 or more, and the\n * next send goes at once, unless it would carry a single record (single records are spaced by the normal minute,\n * so a run reduced to them cannot fire one request after another); otherwise a minute from now.\n */\n function afterSend(r: Run): Next | typeof ASLEEP {\n const waiting = queue.size(r.partition)\n if (waiting === 0) return ASLEEP\n return waiting >= UPLOAD_NOW_RECORDS && r.batchLimit > 1 ? AT_ONCE : AFTER_INTERVAL\n }\n\n /**\n * Too large, or refused as a batch: halve it, and do not grow back to this size until 10 sends have succeeded below\n * it (a proxy's body limit would otherwise refuse every other request, and one spurious refusal would otherwise pin\n * the run to tiny batches). A single record still refused goes, with a gap over its time.\n */\n function shrink(r: Run, records: FleetRecord[]): Next {\n if (records.length > 1) {\n r.batchCap = Math.min(r.batchCap, records.length - 1)\n r.batchLimit = Math.max(1, Math.floor(records.length / 2))\n r.sendsSinceRefusal = 0\n return r.batchLimit > 1 ? AT_ONCE : AFTER_INTERVAL\n }\n const [record] = records\n queue.remove(r.partition, [record.recordId])\n recordRejected(r, record, now())\n // The refusals that narrowed the batch down to this record may have been this record's, not the batch size's.\n r.batchCap = MAX_BATCH_RECORDS\n console.warn(\"FleetReporter: Core refused a single record; it was removed\")\n // Halving cannot help any further. Wait before the next record, so a Core that refuses every batch cannot\n // empty the queue at once.\n return failed(r, null)\n }\n\n // --- the reconciler ------------------------------------------------------------------------------------------\n\n /** The ack's desired-state revision goes to the reconciler. The client may predate the field: read it defensively. */\n function passOnRevision(ack: object): void {\n const revision = (ack as {revision?: unknown}).revision\n if (typeof revision === \"number\" && Number.isSafeInteger(revision) && revision >= 1) signalFleetRevision(revision)\n }\n\n /** An assignment's state changed on this phone: queue it for the run's account, or keep it for the next run. */\n function onOutcome(outcome: FleetAssignmentOutcomePayload): void {\n if (!started) return\n const parsed = FleetAssignmentOutcomePayloadSchema.safeParse(outcome)\n if (!parsed.success) {\n console.warn(\"FleetReporter: an assignment outcome was not a valid record and was left out\")\n return\n }\n const r = run\n if (r && !r.ended) {\n recordOutcome(r, parsed.data)\n return\n }\n // The newest state of an assignment replaces what was kept for it.\n heldOutcomes.delete(parsed.data.assignmentId)\n heldOutcomes.set(parsed.data.assignmentId, parsed.data)\n if (heldOutcomes.size > MAX_HELD_OUTCOMES) heldOutcomes.delete(heldOutcomes.keys().next().value as string)\n }\n\n function recordOutcome(r: Run, payload: FleetAssignmentOutcomePayload): void {\n try {\n collectorQueue.enqueue(r.partition, {\n schemaVersion: FLEET_SCHEMA_VERSION,\n recordId: newId(),\n phoneDeviceId: phoneDeviceId(),\n collectorSessionId: r.collectorSessionId,\n sequence: queue.nextSequence(r.partition, r.collectorSessionId),\n observedAt: iso(now()),\n // An outcome is the phone's own state, measured by no capability.\n capabilities: [],\n kind: \"assignment_outcome\",\n payload,\n })\n } catch (error) {\n warn(\"recording an assignment outcome\", error)\n }\n }\n\n // --- coverage gaps -------------------------------------------------------------------------------------------\n\n function enqueueGap(r: Run, payload: GapPayload, at: number): void {\n try {\n queue.enqueue(r.partition, {\n schemaVersion: FLEET_SCHEMA_VERSION,\n recordId: newId(),\n phoneDeviceId: phoneDeviceId(),\n collectorSessionId: r.collectorSessionId,\n sequence: queue.nextSequence(r.partition, r.collectorSessionId),\n observedAt: iso(at),\n // A gap measures nothing.\n capabilities: [],\n kind: \"coverage_gap\",\n payload,\n })\n } catch (error) {\n warn(\"recording a coverage gap\", error)\n }\n }\n\n function recordRejected(r: Run, record: FleetRecord, at: number): void {\n // A refused gap gets no gap of its own, so a rejection can never feed itself.\n if (record.kind === \"coverage_gap\") return\n const time = iso(timeOf(record.observedAt) ?? at)\n enqueueGap(r, {reason: \"record_rejected\", from: time, to: time, droppedRecordCount: 1}, at)\n }\n\n /**\n * One gap for what the queue dropped (it was full) or expired (7 days old). When no lost record had a readable\n * time, the gap covers the 7 days before now: the longest a record can wait in the queue.\n */\n function recordLosses(r: Run, losses: FleetLosses, at: number): void {\n const count = losses.dropped + losses.expired\n if (count <= 0) return\n const fromMs = timeOf(losses.from)\n const toMs = timeOf(losses.to)\n const end = toMs ?? Math.max(at, fromMs ?? at)\n const start = fromMs ?? end - FLEET_LIMITS.phoneQueueMaxAgeMs\n enqueueGap(\n r,\n {\n reason: losses.dropped > 0 ? \"queue_dropped\" : \"queue_expired\",\n from: iso(Math.min(start, end)),\n to: iso(Math.max(start, end)),\n droppedRecordCount: count,\n },\n at,\n )\n }\n\n // --- the app -------------------------------------------------------------------------------------------------\n\n function onAppState(state: string): void {\n // iOS may suspend the process once it is in the background: checkpoint and try once. Nothing is promised after.\n if (state !== \"background\") return\n const r = run\n if (!r) return\n collector.flushCheckpoint()\n if (r.uploading || r.backingOff) return\n if (r.timer !== null) BgTimer.clearTimeout(r.timer)\n r.timer = null\n void upload(r)\n }\n\n return {\n start() {\n if (started) return\n started = true\n notInstalled = null\n fleetEvents.discardUndeliveredLaunches()\n heldOutcomes.clear()\n guard(\"sweeping the queue\", () => {\n lastSweepAt = now()\n queue.sweepExpired(lastSweepAt)\n })\n guard(\"listening for assignment outcomes\", () => {\n cleanups.push(\n onAssignmentOutcome((outcome) => guard(\"recording an assignment outcome\", () => onOutcome(outcome))),\n )\n })\n guard(\"listening for account changes\", () => {\n cleanups.push(\n cloud.onCoreSessionChanged((change) => guard(\"following a session change\", () => onSessionChanged(change))),\n )\n })\n if (Platform.OS === \"ios\") {\n guard(\"listening for the app state\", () => {\n const subscription = AppState.addEventListener(\"change\", (state) =>\n guard(\"going to the background\", () => onAppState(state)),\n )\n cleanups.push(() => subscription.remove())\n })\n }\n guard(\"starting\", () => {\n // Known at once in the usual case (the engine synced the token just before), so the collector is listening\n // before engine.start() returns, and so before the host restores miniapps.\n const session = cloud.getCoreSession()\n if (session?.mentraUserId) applySession(session)\n else void resolveAndApply()\n })\n },\n\n async stop() {\n if (!started) return\n started = false\n resolution += 1\n cancelResolveRetry()\n resolveFailures = 0\n guard(\"stopping\", () => endRun(\"engine_stopped\"))\n for (const cleanup of cleanups.splice(0)) guard(\"removing a listener\", cleanup)\n notInstalled = null\n fleetEvents.discardUndeliveredLaunches()\n heldOutcomes.clear()\n },\n }\n}\n\nlet appReporter: FleetReporter | null = null\n\n/** Starts the app's reporter, over the shared queue and the engine's Core client. Never throws. */\nexport function startFleetReporter(): void {\n try {\n appReporter ??= createFleetReporter()\n appReporter.start()\n } catch (error) {\n warn(\"start\", error)\n }\n}\n\n/** Stops the app's reporter. */\nexport async function stopFleetReporter(): Promise<void> {\n await appReporter?.stop()\n}\n"]}
|
|
1
|
+
{"version":3,"file":"FleetReporter.js","sourceRoot":"","sources":["../../../src/services/fleet/FleetReporter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Fleet reporter: runs the collector for the signed-in account and delivers its records to the phone's own Core.\n *\n * A run is one account on one Core, which is one partition of the durable queue. Each run starts the collector with\n * a new collector session and uploads only its own partition, as that account, to that Core: every request goes\n * through `reportAs`, which sends nothing unless the client still holds that account's token for that Core. A\n * sign-out, a sign-in or a client rebuilt for another Core ends the run (the collector writes its final checkpoints\n * into the old partition), and the next run starts once the client says whose token it holds. When it cannot say yet\n * (offline, Core unavailable) the account is not known, which is not a sign-out: the reporter asks again after the\n * same backoff as uploads, until it is answered, a sign-out comes, or the engine stops. An old partition's records\n * wait in the queue until their account signs in to their Core again, or expire into a coverage gap; they are never\n * sent as anyone else.\n *\n * A miniapp launch the engine makes while no run is collecting (it restores miniapps while the account may still be\n * being resolved) is kept by `fleetEvents` for the next run, which is that account's. A sign-in or sign-out discards\n * it: what happened before belongs to no one the next run collects for. So do the engine's start and stop.\n *\n * Uploads go a minute after records arrive, or at once when 100 are waiting, at most 500 records (and 1 MB of them)\n * per request. With nothing waiting the uploader sets no timer: the next record wakes it. What each answer does:\n * - accepted or duplicate: the record is removed. Rejected: removed, with a `record_rejected` gap over its\n * observation time. Retryable: kept, and the next request waits at least its Retry-After.\n * - not installed: collection stops and the partition is dropped, until the next sign-in or engine start.\n * - no Fleet route (a 404 without Fleet's code: a Core from before Fleet, or a proxy): collection stops until the next\n * sign-in or engine start, as for not installed, but the partition is kept; its records expire after 7 days.\n * - unavailable, no answer within a minute, an authentication failure, or anything unexpected: everything is kept,\n * and the next attempt waits min(30 min, 30 s * 2^n) +-20%, or Retry-After when given (at most 30 minutes). Only\n * an answer that settles a record (accepted, duplicate or rejected) resets n.\n * - forbidden: everything is kept, and the next attempt waits 30 minutes.\n * - too large, or refused as a batch: the batch is halved; a single record still refused is removed with a gap. The\n * batch does not grow back to a refused size until 10 sends have succeeded below it.\n *\n * After any failure the next batch is a probe of 5 records, and each success doubles the batch again, up to 500. A\n * batch of a single record is never sent straight after another request: it waits the normal minute.\n *\n * Before each upload, the records the queue dropped or expired become one coverage gap, queued in the same tick.\n *\n * The reporter also carries two things between Fleet and the reconciler (`fleetRevisionSignal`):\n * - an ack that names a desired-state `revision` is passed on with `signalFleetRevision`, whatever else the ack says\n * (an ack without one signals nothing; the reconciler ignores a revision it already applied);\n * - each assignment outcome the reconciler emits becomes an `assignment_outcome` record in the current run's\n * partition, once it is a valid payload. The outcome carries no account, so, like a launch, one heard while no run\n * is collecting is kept (the latest per assignment) for the next run, and a sign-in, sign-out or the engine's start\n * and stop discards it: it is never sent as anyone but the account running when it was heard.\n *\n * Fleet is decided by the phone's Core alone. The reporter reads no deployment or telemetry setting, and nothing it\n * does can block the engine: every entry point catches its own failures.\n */\nimport {\n FLEET_LIMITS,\n FLEET_SCHEMA_VERSION,\n FleetAssignmentOutcomePayloadSchema,\n type FleetAssignmentOutcomePayload,\n type FleetRecord,\n} from \"@mentra/cloud-protocol\"\nimport {AppState, Platform} from \"react-native\"\n\nimport {BgTimer} from \"../../utils/timers\"\nimport {\n cloudClientService,\n type CoreSession,\n type CoreSessionChange,\n type FleetSendOutcome,\n} from \"../CloudClientService\"\nimport {createFleetCollector, type FleetCollector} from \"./fleetCollector\"\nimport {fleetEvents} from \"./fleetEvents\"\nimport {newId, phoneDeviceId} from \"./fleetIds\"\nimport {createFleetQueue, partitionKey, type FleetLosses, type FleetQueue} from \"./fleetQueue\"\nimport {onAssignmentOutcome, signalFleetRevision} from \"./fleetRevisionSignal\"\n\nexport interface FleetReporter {\n /** Sweeps the queue, starts collecting for the signed-in account (at once when it is known) and starts uploading. */\n start(): void\n /** Ends collection (`engine_stopped`), abandons a request in flight and drops every timer and listener. */\n stop(): Promise<void>\n}\n\ntype CloudClient = typeof cloudClientService\n/** What the reporter uses of the engine's Core client. */\nexport type FleetReporterCloud = Pick<CloudClient, \"getCoreSession\" | \"resolveCoreSession\" | \"onCoreSessionChanged\"> & {\n core: {fleet: Pick<CloudClient[\"core\"][\"fleet\"], \"reportAs\">}\n}\n\nexport interface FleetReporterDeps {\n /** Defaults to the engine's Core client. */\n cloud?: FleetReporterCloud\n /** Defaults to the app's shared queue. */\n queue?: FleetQueue\n /** Builds the collector over the queue the reporter hands it. Defaults to the engine's collector. */\n createCollector?: (queue: FleetQueue) => FleetCollector\n now?: () => number\n random?: () => number\n}\n\nconst UPLOAD_INTERVAL_MS = 60_000\n/** This many records waiting are sent at once rather than at the next minute. */\nconst UPLOAD_NOW_RECORDS = 100\nconst MAX_BATCH_RECORDS = FLEET_LIMITS.maxRecordsPerBatch\n/** After a failure the next batch is this small, and doubles with each success. */\nconst PROBE_BATCH_RECORDS = 5\n/** A batch size Core refused is not tried again until this many sends have succeeded below it. */\nconst BATCH_CAP_RESET_SENDS = 10\n/** The records' own JSON; the rest of the 1 MiB request limit is headroom for the envelope and the commas. */\nconst MAX_BATCH_BYTES = 1_000_000\nconst BACKOFF_BASE_MS = 30_000\nconst BACKOFF_MAX_MS = 30 * 60_000\n/** A Retry-After of 0 still waits this long, so a server that keeps answering 0 cannot make the loop spin. */\nconst MIN_RETRY_MS = 1_000\n/** `sweepExpired` scans the whole store, so it runs at start and then at most this often. */\nconst SWEEP_INTERVAL_MS = 60 * 60_000\n/** A request still unanswered after this long is abandoned, and counts as no answer. */\nconst REQUEST_TIMEOUT_MS = 60_000\n/** Outcomes heard while no run is collecting: one per assignment, so more than this are not one boot's worth. */\nconst MAX_HELD_OUTCOMES = 64\nconst NO_ANSWER: FleetSendOutcome = {kind: \"unavailable\", retryAfterSeconds: null}\n\n/** min(30 min, 30 s * 2^failures), times a jitter between 0.8 and 1.2. */\nexport function backoffDelayMs(failures: number, random: () => number): number {\n const base = Math.min(BACKOFF_MAX_MS, BACKOFF_BASE_MS * 2 ** Math.max(0, failures))\n return Math.round(base * (0.8 + 0.4 * random()))\n}\n\n/** A server's Retry-After, as a delay the loop can live with. */\nfunction retryDelayMs(seconds: number): number {\n return Math.min(BACKOFF_MAX_MS, Math.max(MIN_RETRY_MS, seconds * 1_000))\n}\n\nfunction iso(wall: number): string {\n return new Date(wall).toISOString()\n}\n\nfunction timeOf(value: string | null): number | null {\n if (value === null) return null\n const ms = Date.parse(value)\n return Number.isNaN(ms) ? null : ms\n}\n\nfunction warn(what: string, error: unknown): void {\n // Only the failure's message: never a record, an account or a token.\n console.warn(`FleetReporter: ${what} failed:`, error instanceof Error ? error.message : \"unknown error\")\n}\n\ntype GapPayload = Extract<FleetRecord, {kind: \"coverage_gap\"}>[\"payload\"]\n/** Fleet's answer for one record, as the Core client read it. */\ntype AckResult = Extract<FleetSendOutcome, {kind: \"results\"}>[\"ack\"][\"results\"][number]\n\n/** One account on one Core, from the start of its collection to the end. */\ninterface Run {\n /** Who the run sends as. The Core URL follows the client's spelling of the same Core. */\n session: {coreUrl: string; mentraUserId: string}\n readonly partition: string\n readonly collectorSessionId: string\n /** Aborted when the run ends, which abandons its request in flight. */\n readonly controller: AbortController\n ended: boolean\n timer: number | null\n /** When the next attempt is due (`now()` time). */\n dueAt: number\n /** The next attempt waits out a backoff the server or a failure asked for. */\n backingOff: boolean\n uploading: boolean\n /** Failed attempts since the last answer that settled a record: the n of the backoff. */\n failures: number\n batchLimit: number\n /** Below the batch sizes Core refused lately: the limit does not grow back to a refused size for a while. */\n batchCap: number\n /** Sends that succeeded since Core last refused a batch; at BATCH_CAP_RESET_SENDS the cap is lifted. */\n sendsSinceRefusal: number\n batchRefusalLogged: boolean\n}\n\n/** When to try next, and whether that wait is a backoff. */\ninterface Next {\n delayMs: number\n backingOff: boolean\n}\nconst AFTER_INTERVAL: Next = {delayMs: UPLOAD_INTERVAL_MS, backingOff: false}\nconst AT_ONCE: Next = {delayMs: 0, backingOff: false}\n/** Nothing is waiting: the uploader sets no timer until a record arrives. */\nconst ASLEEP = null\n\nexport function createFleetReporter(deps: FleetReporterDeps = {}): FleetReporter {\n const cloud = deps.cloud ?? cloudClientService\n const queue = deps.queue ?? createFleetQueue()\n const now = deps.now ?? Date.now\n const random = deps.random ?? Math.random\n\n let started = false\n let run: Run | null = null\n /** The partition its Core said has no Fleet: nothing is collected for it until a sign-in or the next start. */\n let notInstalled: string | null = null\n /** Bumped by every change of session, so only the latest resolution of the account is applied. */\n let resolution = 0\n /** Asks again who the client speaks for, after a resolution that could not tell (offline, Core unavailable). */\n let resolveRetry: number | null = null\n /** Resolutions in a row that could not tell: the n of their backoff. */\n let resolveFailures = 0\n let lastSweepAt = 0\n /** Outcomes heard while no run was collecting, the latest per assignment, for the next run (see the header). */\n const heldOutcomes = new Map<string, FleetAssignmentOutcomePayload>()\n const cleanups: Array<() => void> = []\n\n // The collector writes through this view of the queue, which wakes the uploader once 100 records wait. It\n // delegates at call time, so the shared queue stays the only queue over the store.\n const collectorQueue: FleetQueue = {\n enqueue(partition, record) {\n queue.enqueue(partition, record)\n try {\n onEnqueued(partition)\n } catch (error) {\n warn(\"waking the uploader\", error)\n }\n },\n nextBatch: (partition, maxRecords, maxBytes) => queue.nextBatch(partition, maxRecords, maxBytes),\n remove: (partition, recordIds) => queue.remove(partition, recordIds),\n takeLosses: (partition, at) => queue.takeLosses(partition, at),\n sweepExpired: (at) => queue.sweepExpired(at),\n clearPartition: (partition) => queue.clearPartition(partition),\n size: (partition) => queue.size(partition),\n nextSequence: (partition, collectorSessionId) => queue.nextSequence(partition, collectorSessionId),\n noteTelemetryLoss: (partition, packageName, kind, count, at) => queue.noteTelemetryLoss(partition, packageName, kind, count, at),\n takeTelemetryLosses: (partition, at) => queue.takeTelemetryLosses(partition, at),\n }\n const collector = (deps.createCollector ?? ((q: FleetQueue) => createFleetCollector({queue: q})))(collectorQueue)\n\n function guard(what: string, work: () => void): void {\n try {\n work()\n } catch (error) {\n warn(what, error)\n }\n }\n\n // --- runs ----------------------------------------------------------------------------------------------------\n\n function beginRun(session: Run[\"session\"], partition: string): void {\n const r: Run = {\n session,\n partition,\n collectorSessionId: newId(),\n controller: new AbortController(),\n ended: false,\n timer: null,\n dueAt: 0,\n backingOff: false,\n uploading: false,\n failures: 0,\n batchLimit: MAX_BATCH_RECORDS,\n batchCap: MAX_BATCH_RECORDS,\n sendsSinceRefusal: 0,\n batchRefusalLogged: false,\n }\n run = r\n collector.start({partition, collectorSessionId: r.collectorSessionId})\n collector.flushCheckpoint()\n for (const outcome of [...heldOutcomes.values()]) recordOutcome(r, outcome)\n heldOutcomes.clear()\n schedule(r, afterSend(r))\n }\n\n function endRun(reason: \"engine_stopped\" | \"account_changed\" | \"not_installed\"): void {\n const r = run\n if (!r) return\n run = null\n r.ended = true\n if (r.timer !== null) BgTimer.clearTimeout(r.timer)\n r.timer = null\n r.controller.abort()\n // The collector writes its final checkpoints into the run's partition.\n collector.stop(reason)\n }\n\n /**\n * Core said Fleet is not there for this session: nothing more is collected for or sent from the partition until the\n * next sign-in or engine start. Only Fleet's own \"not installed\" drops what was queued; a Core without the route\n * (or a proxy answering for it) proves nothing about Fleet, so its records wait in the queue, and expire there.\n */\n function stopForSession(r: Run, drop: boolean): void {\n notInstalled = r.partition\n endRun(\"not_installed\")\n if (drop) queue.clearPartition(r.partition)\n }\n\n /** Collects for `session` if it names an account; otherwise collects for no one. */\n function applySession(session: CoreSession | null): void {\n if (!started) return\n const user = session?.mentraUserId\n if (!session || !user) {\n endRun(\"account_changed\")\n return\n }\n const partition = partitionKey(session.coreUrl, user)\n if (run?.partition === partition) {\n // The same Core, maybe spelled differently by a rebuilt client: send as the client spells it now.\n if (run.session.coreUrl !== session.coreUrl) run.session = {coreUrl: session.coreUrl, mentraUserId: user}\n return\n }\n endRun(\"account_changed\")\n if (partition === notInstalled) return\n beginRun({coreUrl: session.coreUrl, mentraUserId: user}, partition)\n }\n\n /**\n * Asks the client who it speaks for now (refreshing its token if needed) and follows. The latest ask wins. An ask\n * the client cannot answer yet (offline, Core unavailable) is no sign-out: what is running keeps running, and the\n * ask is made again after a backoff, until it is answered or a session change asks anew.\n */\n async function resolveAndApply(): Promise<void> {\n const mine = ++resolution\n cancelResolveRetry()\n let session: CoreSession | null\n try {\n session = await cloud.resolveCoreSession()\n } catch (error) {\n if (mine !== resolution || !started) return\n warn(\"resolving the account\", error)\n retryResolution()\n return\n }\n if (mine !== resolution || !started) return\n resolveFailures = 0\n guard(\"following the account\", () => applySession(session))\n }\n\n function retryResolution(): void {\n const delayMs = backoffDelayMs(resolveFailures, random)\n resolveFailures += 1\n resolveRetry = BgTimer.setTimeout(() => {\n resolveRetry = null\n void resolveAndApply()\n }, delayMs)\n }\n\n function cancelResolveRetry(): void {\n if (resolveRetry !== null) BgTimer.clearTimeout(resolveRetry)\n resolveRetry = null\n }\n\n function onSessionChanged(change: CoreSessionChange): void {\n if (!started) return\n if (change.reason !== \"reconnect\") {\n fleetEvents.discardUndeliveredLaunches()\n heldOutcomes.clear()\n }\n if (change.reason === \"signed_out\") {\n // A resolution still under way, or one waiting to ask again, must not bring the account back.\n resolution += 1\n cancelResolveRetry()\n resolveFailures = 0\n endRun(\"account_changed\")\n return\n }\n // A new authenticated session asks Core again, including a Core that said it has no Fleet.\n if (change.reason === \"signed_in\") notInstalled = null\n // What the change already names is enough to stop collecting for an account or a Core that is no longer current.\n // A sign-in that names no account yet may be anyone: what follows is not the previous account's until the client\n // says whose token it holds. A rebuilt client that names none yet is still the session that was running.\n const r = run\n if (r) {\n const account = change.mentraUserId ?? (change.reason === \"signed_in\" ? null : r.session.mentraUserId)\n let partition: string | null = null\n try {\n partition = change.coreUrl && account ? partitionKey(change.coreUrl, account) : null\n } catch {\n partition = null\n }\n if (partition !== r.partition) endRun(\"account_changed\")\n }\n void resolveAndApply()\n }\n\n // --- the upload loop -----------------------------------------------------------------------------------------\n\n function schedule(r: Run, next: Next | typeof ASLEEP): void {\n if (r.ended) return\n if (r.timer !== null) BgTimer.clearTimeout(r.timer)\n r.timer = null\n if (next === ASLEEP) {\n r.backingOff = false\n return\n }\n r.backingOff = next.backingOff\n r.dueAt = now() + next.delayMs\n r.timer = BgTimer.setTimeout(() => {\n r.timer = null\n guard(\"upload tick\", () => tick(r))\n }, next.delayMs)\n }\n\n /**\n * A record arrived. An uploader that was asleep is woken (a minute from now, or at once when 100 wait); one that is\n * due anyway is hurried once 100 wait. Deferred to a timer either way: this runs inside whatever the collector is\n * reacting to. While a request is out or the loop is backing off, the next attempt picks the record up.\n */\n function onEnqueued(partition: string): void {\n const r = run\n if (!r || r.partition !== partition || r.uploading || r.backingOff) return\n if (r.timer === null) schedule(r, afterSend(r))\n else if (r.dueAt > now() && queue.size(partition) >= UPLOAD_NOW_RECORDS && r.batchLimit > 1) schedule(r, AT_ONCE)\n }\n\n function sweepIfDue(at: number): void {\n // A clock set back sweeps again rather than waiting for it to catch up.\n if (at >= lastSweepAt && at - lastSweepAt < SWEEP_INTERVAL_MS) return\n lastSweepAt = at\n queue.sweepExpired(at)\n }\n\n function tick(r: Run): void {\n if (r !== run || r.ended) return\n r.backingOff = false\n guard(\"sweeping the queue\", () => sweepIfDue(now()))\n if (!r.uploading) void upload(r)\n }\n\n async function upload(r: Run): Promise<void> {\n r.uploading = true\n let next: Next | typeof ASLEEP\n try {\n next = await attempt(r)\n } catch (error) {\n // Whatever went wrong, the loop goes on: back off and try again.\n warn(\"upload\", error)\n next = failed(r, null)\n } finally {\n r.uploading = false\n }\n if (r === run) schedule(r, next)\n }\n\n async function attempt(r: Run): Promise<Next | typeof ASLEEP> {\n const at = now()\n // takeLosses clears the counts it returns, so their gap is queued before anything else can happen.\n recordLosses(r, queue.takeLosses(r.partition, at), at)\n recordTelemetryLosses(r, at)\n const records = queue.nextBatch(r.partition, r.batchLimit, MAX_BATCH_BYTES)\n if (records.length === 0) return ASLEEP\n let outcome: FleetSendOutcome\n try {\n outcome = await send(r, records)\n } catch (error) {\n if (r.ended) return ASLEEP\n throw error\n }\n // An answer for a run that is over changes nothing: its records are sent again as their own account later.\n if (r.ended) return ASLEEP\n return answered(r, records, outcome)\n }\n\n /**\n * Sends one batch as the run's account. The request is abandoned when the run ends or when it has gone a minute\n * without an answer, so a request that hangs cannot keep the run from ever trying again; an abandoned request is\n * no answer (`unavailable`), and whatever it says later is ignored.\n */\n async function send(r: Run, records: FleetRecord[]): Promise<FleetSendOutcome> {\n const request = new AbortController()\n let timer: number | null = null\n let stopWaiting: () => void = () => {}\n const abandoned = new Promise<FleetSendOutcome>((resolve) => {\n stopWaiting = () => resolve(NO_ANSWER)\n })\n // Lets go of the timer and the run's signal at once, so a run that ends leaves nothing of the request behind.\n const release = () => {\n if (timer !== null) BgTimer.clearTimeout(timer)\n timer = null\n r.controller.signal.removeEventListener(\"abort\", abandon)\n }\n const abandon = () => {\n release()\n request.abort()\n stopWaiting()\n }\n r.controller.signal.addEventListener(\"abort\", abandon)\n timer = BgTimer.setTimeout(() => {\n timer = null\n abandon()\n }, REQUEST_TIMEOUT_MS)\n try {\n if (r.controller.signal.aborted) abandon()\n const answer = cloud.core.fleet.reportAs(\n r.session,\n {schemaVersion: FLEET_SCHEMA_VERSION, records},\n {signal: request.signal},\n )\n // Once abandoned, how the request ends is of no interest, and must not surface as an unhandled rejection.\n answer.catch(() => {})\n return await Promise.race([answer, abandoned])\n } finally {\n release()\n }\n }\n\n function answered(r: Run, records: FleetRecord[], outcome: FleetSendOutcome): Next | typeof ASLEEP {\n switch (outcome.kind) {\n case \"results\":\n guard(\"passing on the ack revision\", () => passOnRevision(outcome.ack))\n return settle(r, records, outcome.ack.results)\n case \"not_installed\":\n stopForSession(r, true)\n return ASLEEP\n case \"no_fleet_route\":\n stopForSession(r, false)\n return ASLEEP\n case \"unavailable\":\n return failed(r, outcome.retryAfterSeconds)\n case \"unauthorized\":\n // The client refreshed the token once; records are never dropped for credentials.\n return failed(r, null)\n case \"forbidden\":\n // Core will not take this account's reports now; that changes slowly, so wait the longest backoff.\n r.batchLimit = Math.min(r.batchLimit, PROBE_BATCH_RECORDS)\n r.failures += 1\n return {delayMs: Math.round(BACKOFF_MAX_MS * (0.8 + 0.4 * random())), backingOff: true}\n case \"batch_too_large\":\n return shrink(r, records)\n case \"batch_invalid\":\n if (!r.batchRefusalLogged) {\n r.batchRefusalLogged = true\n console.warn(`FleetReporter: Core refused a batch (${outcome.code}); sending smaller batches`)\n }\n return shrink(r, records)\n case \"session_changed\":\n // The client speaks for someone else now. Nothing was sent; follow it.\n void resolveAndApply()\n return failed(r, null)\n }\n }\n\n function failed(r: Run, retryAfterSeconds: number | null): Next {\n // Whatever went wrong may still be wrong: the next attempt sends a small batch, not the whole backlog again.\n r.batchLimit = Math.min(r.batchLimit, PROBE_BATCH_RECORDS)\n const delayMs =\n retryAfterSeconds != null && Number.isFinite(retryAfterSeconds)\n ? retryDelayMs(retryAfterSeconds)\n : backoffDelayMs(r.failures, random)\n r.failures += 1\n return {delayMs, backingOff: true}\n }\n\n function settle(r: Run, records: FleetRecord[], results: AckResult[]): Next | typeof ASLEEP {\n const removed: string[] = []\n const rejected: FleetRecord[] = []\n const reasons = new Map<string, number>()\n const answeredIndexes = new Set<number>()\n let retryable = false\n let retryAfterSeconds: number | null = null\n for (const result of results) {\n const record = records[result.index]\n // A result counts only for the record at its index, and only if it names that record.\n if (!record || answeredIndexes.has(result.index)) continue\n if (result.recordId !== undefined && result.recordId !== record.recordId) continue\n answeredIndexes.add(result.index)\n if (result.status === \"accepted\" || result.status === \"duplicate\") {\n removed.push(record.recordId)\n } else if (result.status === \"rejected\") {\n removed.push(record.recordId)\n rejected.push(record)\n const reason = result.reasonCode ?? \"unknown\"\n reasons.set(reason, (reasons.get(reason) ?? 0) + 1)\n } else {\n retryable = true\n if (result.retryAfterSeconds !== undefined) {\n retryAfterSeconds = Math.max(retryAfterSeconds ?? 0, result.retryAfterSeconds)\n }\n }\n }\n // An answer that settles nothing is no answer, and one that only says \"retry\" is a failure to deliver: neither\n // resets the backoff, or a Core answering retryable to everything would get a full batch every 30 s forever.\n if (removed.length === 0) return failed(r, retryable ? retryAfterSeconds : null)\n\n r.failures = 0\n r.sendsSinceRefusal += 1\n if (r.sendsSinceRefusal >= BATCH_CAP_RESET_SENDS) r.batchCap = MAX_BATCH_RECORDS\n r.batchLimit = Math.min(r.batchCap, r.batchLimit * 2)\n const at = now()\n queue.remove(r.partition, removed)\n for (const record of rejected) recordRejected(r, record, at)\n if (rejected.length > 0) {\n const summary = [...reasons].map(([reason, count]) => `${reason} x${count}`).join(\", \")\n console.warn(`FleetReporter: Core rejected ${rejected.length} record(s): ${summary}`)\n }\n if (retryable) {\n return {\n delayMs: retryAfterSeconds !== null ? retryDelayMs(retryAfterSeconds) : backoffDelayMs(0, random),\n backingOff: true,\n }\n }\n return afterSend(r)\n }\n\n /**\n * When to send next, by what waits: nothing, and the uploader sleeps until a record arrives; 100 or more, and the\n * next send goes at once, unless it would carry a single record (single records are spaced by the normal minute,\n * so a run reduced to them cannot fire one request after another); otherwise a minute from now.\n */\n function afterSend(r: Run): Next | typeof ASLEEP {\n const waiting = queue.size(r.partition)\n if (waiting === 0) return ASLEEP\n return waiting >= UPLOAD_NOW_RECORDS && r.batchLimit > 1 ? AT_ONCE : AFTER_INTERVAL\n }\n\n /**\n * Too large, or refused as a batch: halve it, and do not grow back to this size until 10 sends have succeeded below\n * it (a proxy's body limit would otherwise refuse every other request, and one spurious refusal would otherwise pin\n * the run to tiny batches). A single record still refused goes, with a gap over its time.\n */\n function shrink(r: Run, records: FleetRecord[]): Next {\n if (records.length > 1) {\n r.batchCap = Math.min(r.batchCap, records.length - 1)\n r.batchLimit = Math.max(1, Math.floor(records.length / 2))\n r.sendsSinceRefusal = 0\n return r.batchLimit > 1 ? AT_ONCE : AFTER_INTERVAL\n }\n const [record] = records\n queue.remove(r.partition, [record.recordId])\n recordRejected(r, record, now())\n // The refusals that narrowed the batch down to this record may have been this record's, not the batch size's.\n r.batchCap = MAX_BATCH_RECORDS\n console.warn(\"FleetReporter: Core refused a single record; it was removed\")\n // Halving cannot help any further. Wait before the next record, so a Core that refuses every batch cannot\n // empty the queue at once.\n return failed(r, null)\n }\n\n // --- the reconciler ------------------------------------------------------------------------------------------\n\n /** The ack's desired-state revision goes to the reconciler. The client may predate the field: read it defensively. */\n function passOnRevision(ack: object): void {\n const revision = (ack as {revision?: unknown}).revision\n if (typeof revision === \"number\" && Number.isSafeInteger(revision) && revision >= 1) signalFleetRevision(revision)\n }\n\n /** An assignment's state changed on this phone: queue it for the run's account, or keep it for the next run. */\n function onOutcome(outcome: FleetAssignmentOutcomePayload): void {\n if (!started) return\n const parsed = FleetAssignmentOutcomePayloadSchema.safeParse(outcome)\n if (!parsed.success) {\n console.warn(\"FleetReporter: an assignment outcome was not a valid record and was left out\")\n return\n }\n const r = run\n if (r && !r.ended) {\n recordOutcome(r, parsed.data)\n return\n }\n // The newest state of an assignment replaces what was kept for it.\n heldOutcomes.delete(parsed.data.assignmentId)\n heldOutcomes.set(parsed.data.assignmentId, parsed.data)\n if (heldOutcomes.size > MAX_HELD_OUTCOMES) heldOutcomes.delete(heldOutcomes.keys().next().value as string)\n }\n\n function recordOutcome(r: Run, payload: FleetAssignmentOutcomePayload): void {\n try {\n collectorQueue.enqueue(r.partition, {\n schemaVersion: FLEET_SCHEMA_VERSION,\n recordId: newId(),\n phoneDeviceId: phoneDeviceId(),\n collectorSessionId: r.collectorSessionId,\n sequence: queue.nextSequence(r.partition, r.collectorSessionId),\n observedAt: iso(now()),\n // An outcome is the phone's own state, measured by no capability.\n capabilities: [],\n kind: \"assignment_outcome\",\n payload,\n })\n } catch (error) {\n warn(\"recording an assignment outcome\", error)\n }\n }\n\n // --- coverage gaps -------------------------------------------------------------------------------------------\n\n function enqueueGap(r: Run, payload: GapPayload, at: number): void {\n try {\n queue.enqueue(r.partition, {\n schemaVersion: FLEET_SCHEMA_VERSION,\n recordId: newId(),\n phoneDeviceId: phoneDeviceId(),\n collectorSessionId: r.collectorSessionId,\n sequence: queue.nextSequence(r.partition, r.collectorSessionId),\n observedAt: iso(at),\n // A gap measures nothing.\n capabilities: [],\n kind: \"coverage_gap\",\n payload,\n })\n } catch (error) {\n warn(\"recording a coverage gap\", error)\n }\n }\n\n /**\n * The miniapp telemetry lost since the last upload, per package, as one `telemetry_loss` record. Telemetry losses\n * never become coverage gaps. Counts that cannot be queued are lost with the events they counted.\n */\n function recordTelemetryLosses(r: Run, at: number): void {\n const losses = queue.takeTelemetryLosses(r.partition, at)\n if (losses === null) return\n const entries = Object.entries(losses.packages)\n .map(([packageName, counts]) => ({packageName, ...counts}))\n .filter((loss) => loss.overLimit + loss.evicted + loss.expired + loss.rejected > 0)\n .slice(0, FLEET_LIMITS.maxTelemetryLossPackages)\n if (entries.length === 0) return\n const end = iso(at)\n const from = losses.from !== null && timeOf(losses.from) !== null && timeOf(losses.from)! <= at ? losses.from : end\n try {\n queue.enqueue(r.partition, {\n schemaVersion: FLEET_SCHEMA_VERSION,\n recordId: newId(),\n phoneDeviceId: phoneDeviceId(),\n collectorSessionId: r.collectorSessionId,\n sequence: queue.nextSequence(r.partition, r.collectorSessionId),\n observedAt: end,\n capabilities: [\"miniapp_telemetry\"],\n kind: \"telemetry_loss\",\n payload: {from, to: end, losses: entries},\n })\n } catch (error) {\n warn(\"recording telemetry losses\", error)\n }\n }\n\n function recordRejected(r: Run, record: FleetRecord, at: number): void {\n // A refused gap gets no gap of its own, so a rejection can never feed itself.\n if (record.kind === \"coverage_gap\") return\n // Rejected telemetry is counted for its miniapp, never as a gap; a refused loss report is not counted again.\n if (record.kind === \"telemetry_event\") {\n queue.noteTelemetryLoss(r.partition, record.payload.packageName, \"rejected\", 1, at)\n return\n }\n if (record.kind === \"telemetry_loss\") return\n const time = iso(timeOf(record.observedAt) ?? at)\n enqueueGap(r, {reason: \"record_rejected\", from: time, to: time, droppedRecordCount: 1}, at)\n }\n\n /**\n * One gap for what the queue dropped (it was full) or expired (7 days old). When no lost record had a readable\n * time, the gap covers the 7 days before now: the longest a record can wait in the queue.\n */\n function recordLosses(r: Run, losses: FleetLosses, at: number): void {\n const count = losses.dropped + losses.expired\n if (count <= 0) return\n const fromMs = timeOf(losses.from)\n const toMs = timeOf(losses.to)\n const end = toMs ?? Math.max(at, fromMs ?? at)\n const start = fromMs ?? end - FLEET_LIMITS.phoneQueueMaxAgeMs\n enqueueGap(\n r,\n {\n reason: losses.dropped > 0 ? \"queue_dropped\" : \"queue_expired\",\n from: iso(Math.min(start, end)),\n to: iso(Math.max(start, end)),\n droppedRecordCount: count,\n },\n at,\n )\n }\n\n // --- the app -------------------------------------------------------------------------------------------------\n\n function onAppState(state: string): void {\n // iOS may suspend the process once it is in the background: checkpoint and try once. Nothing is promised after.\n if (state !== \"background\") return\n const r = run\n if (!r) return\n collector.flushCheckpoint()\n if (r.uploading || r.backingOff) return\n if (r.timer !== null) BgTimer.clearTimeout(r.timer)\n r.timer = null\n void upload(r)\n }\n\n return {\n start() {\n if (started) return\n started = true\n notInstalled = null\n fleetEvents.discardUndeliveredLaunches()\n heldOutcomes.clear()\n guard(\"sweeping the queue\", () => {\n lastSweepAt = now()\n queue.sweepExpired(lastSweepAt)\n })\n guard(\"listening for assignment outcomes\", () => {\n cleanups.push(\n onAssignmentOutcome((outcome) => guard(\"recording an assignment outcome\", () => onOutcome(outcome))),\n )\n })\n guard(\"listening for account changes\", () => {\n cleanups.push(\n cloud.onCoreSessionChanged((change) => guard(\"following a session change\", () => onSessionChanged(change))),\n )\n })\n if (Platform.OS === \"ios\") {\n guard(\"listening for the app state\", () => {\n const subscription = AppState.addEventListener(\"change\", (state) =>\n guard(\"going to the background\", () => onAppState(state)),\n )\n cleanups.push(() => subscription.remove())\n })\n }\n guard(\"starting\", () => {\n // Known at once in the usual case (the engine synced the token just before), so the collector is listening\n // before engine.start() returns, and so before the host restores miniapps.\n const session = cloud.getCoreSession()\n if (session?.mentraUserId) applySession(session)\n else void resolveAndApply()\n })\n },\n\n async stop() {\n if (!started) return\n started = false\n resolution += 1\n cancelResolveRetry()\n resolveFailures = 0\n guard(\"stopping\", () => endRun(\"engine_stopped\"))\n for (const cleanup of cleanups.splice(0)) guard(\"removing a listener\", cleanup)\n notInstalled = null\n fleetEvents.discardUndeliveredLaunches()\n heldOutcomes.clear()\n },\n }\n}\n\nlet appReporter: FleetReporter | null = null\n\n/** Starts the app's reporter, over the shared queue and the engine's Core client. Never throws. */\nexport function startFleetReporter(): void {\n try {\n appReporter ??= createFleetReporter()\n appReporter.start()\n } catch (error) {\n warn(\"start\", error)\n }\n}\n\n/** Stops the app's reporter. */\nexport async function stopFleetReporter(): Promise<void> {\n await appReporter?.stop()\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type FleetInstallReason } from "@mentra/cloud-protocol";
|
|
2
|
-
import type
|
|
2
|
+
import { type FleetQueue } from "./fleetQueue";
|
|
3
3
|
export interface FleetCollector {
|
|
4
4
|
/**
|
|
5
5
|
* Opens the engine session, the glasses and running miniapps, and the foreground session once the app has been
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleetCollector.d.ts","sourceRoot":"","sources":["../../../src/services/fleet/fleetCollector.ts"],"names":[],"mappings":"AAyDA,OAAO,EAQL,KAAK,kBAAkB,EAGxB,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"fleetCollector.d.ts","sourceRoot":"","sources":["../../../src/services/fleet/fleetCollector.ts"],"names":[],"mappings":"AAyDA,OAAO,EAQL,KAAK,kBAAkB,EAGxB,MAAM,wBAAwB,CAAA;AAgB/B,OAAO,EAA0B,KAAK,UAAU,EAAC,MAAM,cAAc,CAAA;AAYrE,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,CAAC,GAAG,EAAE;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAA;IACjE,0GAA0G;IAC1G,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,eAAe,GAAG,IAAI,CAAA;IAC1E,mEAAmE;IACnE,eAAe,IAAI,IAAI,CAAA;CACxB;AAED,8HAA8H;AAC9H,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAA;IACd,yHAAyH;IACzH,OAAO,EAAE,SAAS,kBAAkB,EAAE,CAAA;CACvC;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,iHAAiH;IACjH,IAAI,IAAI,SAAS,gBAAgB,EAAE,GAAG,IAAI,CAAA;IAC1C,uHAAuH;IACvH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;CAC5C;AAED,iFAAiF;AACjF,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,mHAAmH;IACnH,IAAI,IAAI,mBAAmB,GAAG,aAAa,GAAG,IAAI,CAAA;IAClD,sFAAsF;IACtF,OAAO,IAAI,IAAI,CAAA;IACf,qEAAqE;IACrE,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;CAC5C;AA0ZD,sEAAsE;AACtE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAY5E;AAqiCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,KAAK,EAAE,UAAU,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAA;IACxB,oBAAoB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,sHAAsH;IACtH,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,yFAAyF;IACzF,YAAY,CAAC,EAAE,kBAAkB,CAAA;CAClC,GAAG,cAAc,CAmDjB"}
|
|
@@ -60,8 +60,10 @@ import { useGlassesStore } from "../../stores/glasses";
|
|
|
60
60
|
import { BgTimer } from "../../utils/timers";
|
|
61
61
|
import { miniappRunningRegistry } from "../MiniappRunningRegistry";
|
|
62
62
|
import { nativeMiniappExecution } from "../NativeMiniappExecution";
|
|
63
|
-
import { fleetEvents } from "./fleetEvents";
|
|
63
|
+
import { fleetEvents, } from "./fleetEvents";
|
|
64
64
|
import { newId, phoneDeviceId } from "./fleetIds";
|
|
65
|
+
import { TELEMETRY_RECORD_PREFIX } from "./fleetQueue";
|
|
66
|
+
import { limitEvent, TelemetryRate } from "./telemetryLimits";
|
|
65
67
|
import { appPhoneBattery } from "./phoneBatterySource";
|
|
66
68
|
import { glassesProduct, isSimulatedGlasses, provisionalBinding, serialBinding, usableSerial, } from "./glassesIdentity";
|
|
67
69
|
const FAST_CHECKPOINT_MS = 60_000;
|
|
@@ -114,6 +116,8 @@ const MODELS_WITHOUT_BATTERY = new Set();
|
|
|
114
116
|
* What the phone measures without glasses. With glasses connected it measures all of them, less the battery of glasses
|
|
115
117
|
* that cannot report one. A phone that cannot report its own battery does not claim `phone_battery`.
|
|
116
118
|
*/
|
|
119
|
+
/** A disconnect this soon after the person asked for one is theirs. */
|
|
120
|
+
const USER_DISCONNECT_WINDOW_MS = 10_000;
|
|
117
121
|
const PHONE_CAPABILITIES = [
|
|
118
122
|
"glasses_connection",
|
|
119
123
|
"miniapp_execution",
|
|
@@ -121,6 +125,10 @@ const PHONE_CAPABILITIES = [
|
|
|
121
125
|
"engine_active",
|
|
122
126
|
"photo_operations",
|
|
123
127
|
"phone_battery",
|
|
128
|
+
// Phase 12: what this Mentra App measures whatever is connected.
|
|
129
|
+
"miniapp_failures",
|
|
130
|
+
"glasses_connectivity",
|
|
131
|
+
"miniapp_telemetry",
|
|
124
132
|
];
|
|
125
133
|
function warn(what, error) {
|
|
126
134
|
// Only the failure's message: an error from here must never carry a package, a serial or a record into the logs.
|
|
@@ -380,6 +388,12 @@ class CollectorRun {
|
|
|
380
388
|
lastSnapshotMono = 0;
|
|
381
389
|
lastSnapshotKey = null;
|
|
382
390
|
known = null;
|
|
391
|
+
/** The store's last connection state, and when the glasses last dropped (for a reconnect's time). */
|
|
392
|
+
rawConnection = null;
|
|
393
|
+
disconnectedAt = null;
|
|
394
|
+
connectFailed = false;
|
|
395
|
+
/** Each miniapp's telemetry rate on this phone, for this run. */
|
|
396
|
+
telemetryRate = new TelemetryRate();
|
|
383
397
|
heldPhotos = [];
|
|
384
398
|
battery = null;
|
|
385
399
|
phoneBattery = null;
|
|
@@ -404,6 +418,10 @@ class CollectorRun {
|
|
|
404
418
|
this.cleanups.push(useGlassesStore.subscribe(glassesFingerprint, () => this.guard("glasses change", () => this.onStateChange())), miniappRunningRegistry.subscribe(() => this.guard("miniapp change", () => this.onStateChange())), nativeMiniappExecution.subscribe(() => this.guard("miniapp change", () => this.onStateChange())), fleetEvents.subscribe({
|
|
405
419
|
launch: (event) => this.guard("launch", () => this.onLaunch(event)),
|
|
406
420
|
photo: (event) => this.guard("photo", () => this.onPhoto(event)),
|
|
421
|
+
telemetry: (event) => this.guard("telemetry", () => this.onTelemetry(event)),
|
|
422
|
+
crash: (event) => this.guard("crash", () => this.onCrash(event)),
|
|
423
|
+
install: (event) => this.guard("install", () => this.onInstall(event)),
|
|
424
|
+
connection: (event) => this.guard("connection", () => this.onConnection(event)),
|
|
407
425
|
}));
|
|
408
426
|
const appState = AppState.addEventListener("change", (state) => this.guard("app state", () => {
|
|
409
427
|
if (state !== "active")
|
|
@@ -493,10 +511,44 @@ class CollectorRun {
|
|
|
493
511
|
this.releasePhotos();
|
|
494
512
|
this.evaluateSnapshotTriggers();
|
|
495
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Phase 12's connection events, from the store's own states: connected to disconnected is a disconnect (the
|
|
516
|
+
* person's own within 10 s of their asking, otherwise a lost link); connecting or bonding to disconnected without
|
|
517
|
+
* connecting is a failed connection, once until the glasses connect again and never while a lost link waits to come
|
|
518
|
+
* back; connected again after a disconnect is a reconnect, with the time it took.
|
|
519
|
+
* Simulated glasses have none.
|
|
520
|
+
*/
|
|
521
|
+
noteConnectionChange(raw, simulated, wall) {
|
|
522
|
+
const previous = this.rawConnection;
|
|
523
|
+
this.rawConnection = raw;
|
|
524
|
+
if (previous === null || previous === raw || simulated)
|
|
525
|
+
return;
|
|
526
|
+
if (raw === "disconnected" && previous === "connected") {
|
|
527
|
+
const theirs = wall - fleetEvents.lastUserDisconnect() < USER_DISCONNECT_WINDOW_MS;
|
|
528
|
+
this.onConnection({ event: "disconnected", category: theirs ? "user_action" : "link_lost" });
|
|
529
|
+
this.disconnectedAt = theirs ? null : wall;
|
|
530
|
+
}
|
|
531
|
+
else if (raw === "disconnected" && (previous === "connecting" || previous === "bonding")) {
|
|
532
|
+
// Glasses away retry all the time: a lost link waiting to come back is its disconnect, and only the first failed
|
|
533
|
+
// connection between two connected spells is one, so an hour of retries is not an hour of records.
|
|
534
|
+
if (this.disconnectedAt === null && !this.connectFailed) {
|
|
535
|
+
this.connectFailed = true;
|
|
536
|
+
this.onConnection({ event: "connect_failed", category: "unknown" });
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
else if (raw === "connected") {
|
|
540
|
+
this.connectFailed = false;
|
|
541
|
+
if (this.disconnectedAt !== null) {
|
|
542
|
+
this.onConnection({ event: "reconnected", category: "unknown", reconnectMs: wall - this.disconnectedAt });
|
|
543
|
+
this.disconnectedAt = null;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
496
547
|
/** Notes what the glasses store says now: the connection, the glasses' identity and versions, and the battery. */
|
|
497
548
|
trackGlasses(wall) {
|
|
498
549
|
const s = useGlassesStore.getState();
|
|
499
550
|
const state = mapConnection(s.connection.state);
|
|
551
|
+
this.noteConnectionChange(s.connection.state, isSimulatedGlasses(s.deviceModel), wall);
|
|
500
552
|
if (state !== this.connection.state) {
|
|
501
553
|
// Glasses that start connecting are not assumed to be the pair that was last here: a new connection is
|
|
502
554
|
// identified afresh, from what the glasses say, before anything is attributed to a device. Only a new
|
|
@@ -1179,6 +1231,95 @@ class CollectorRun {
|
|
|
1179
1231
|
...(versionName ? { versionName } : {}),
|
|
1180
1232
|
start: event.start,
|
|
1181
1233
|
outcome: event.outcome,
|
|
1234
|
+
...(event.outcome === "failed" && event.failureReason ? { failureReason: event.failureReason } : {}),
|
|
1235
|
+
},
|
|
1236
|
+
}));
|
|
1237
|
+
}
|
|
1238
|
+
// --- Phase 12: a miniapp's own events, crashes, installs and connection changes -------------------------------
|
|
1239
|
+
/** The open execution session of `packageName`, which a telemetry event or crash is attributed to. */
|
|
1240
|
+
executionOf(packageName) {
|
|
1241
|
+
return this.sessions.get(`miniapp_execution:${packageName}`)?.id;
|
|
1242
|
+
}
|
|
1243
|
+
onTelemetry(event) {
|
|
1244
|
+
const packageName = text(event.packageName);
|
|
1245
|
+
if (!packageName)
|
|
1246
|
+
return;
|
|
1247
|
+
const at = this.env.now();
|
|
1248
|
+
const limited = limitEvent(event.event, event.properties);
|
|
1249
|
+
if (limited === null || !this.telemetryRate.admit(packageName, at)) {
|
|
1250
|
+
this.env.queue.noteTelemetryLoss(this.partition, packageName, "overLimit", 1, at);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
const versionName = text(event.versionName);
|
|
1254
|
+
const executionSessionId = this.executionOf(packageName);
|
|
1255
|
+
this.emit(at, (base) => ({
|
|
1256
|
+
...base,
|
|
1257
|
+
kind: "telemetry_event",
|
|
1258
|
+
payload: {
|
|
1259
|
+
packageName,
|
|
1260
|
+
...(versionName ? { versionName } : {}),
|
|
1261
|
+
...(executionSessionId ? { executionSessionId } : {}),
|
|
1262
|
+
event: limited.event,
|
|
1263
|
+
properties: limited.properties,
|
|
1264
|
+
droppedProperties: limited.droppedProperties,
|
|
1265
|
+
cutValues: limited.cutValues,
|
|
1266
|
+
},
|
|
1267
|
+
}), TELEMETRY_RECORD_PREFIX);
|
|
1268
|
+
}
|
|
1269
|
+
onCrash(event) {
|
|
1270
|
+
const packageName = text(event.packageName);
|
|
1271
|
+
if (!packageName)
|
|
1272
|
+
return;
|
|
1273
|
+
const versionName = text(event.versionName);
|
|
1274
|
+
const executionSessionId = this.executionOf(packageName);
|
|
1275
|
+
this.emit(this.env.now(), (base) => ({
|
|
1276
|
+
...base,
|
|
1277
|
+
kind: "miniapp_crash",
|
|
1278
|
+
payload: {
|
|
1279
|
+
packageName,
|
|
1280
|
+
...(versionName ? { versionName } : {}),
|
|
1281
|
+
...(executionSessionId ? { executionSessionId } : {}),
|
|
1282
|
+
surface: event.surface,
|
|
1283
|
+
category: event.category,
|
|
1284
|
+
outcome: event.outcome,
|
|
1285
|
+
crashesInWindow: Math.min(FLEET_LIMITS.maxCrashesInWindow, Math.max(1, Math.round(event.crashesInWindow))),
|
|
1286
|
+
},
|
|
1287
|
+
}));
|
|
1288
|
+
}
|
|
1289
|
+
onInstall(event) {
|
|
1290
|
+
const packageName = text(event.packageName);
|
|
1291
|
+
if (!packageName)
|
|
1292
|
+
return;
|
|
1293
|
+
const fromVersion = text(event.fromVersion);
|
|
1294
|
+
const toVersion = text(event.toVersion);
|
|
1295
|
+
this.emit(this.env.now(), (base) => ({
|
|
1296
|
+
...base,
|
|
1297
|
+
kind: "miniapp_install",
|
|
1298
|
+
payload: {
|
|
1299
|
+
packageName,
|
|
1300
|
+
operation: event.operation,
|
|
1301
|
+
source: event.source,
|
|
1302
|
+
...(fromVersion ? { fromVersion } : {}),
|
|
1303
|
+
...(toVersion ? { toVersion } : {}),
|
|
1304
|
+
outcome: event.outcome,
|
|
1305
|
+
...(event.outcome === "succeeded" ? {} : { reason: event.reason ?? "unknown" }),
|
|
1306
|
+
},
|
|
1307
|
+
}));
|
|
1308
|
+
}
|
|
1309
|
+
onConnection(event) {
|
|
1310
|
+
const binding = this.currentBinding() ?? this.known?.binding ?? null;
|
|
1311
|
+
const glassesModel = this.known?.model;
|
|
1312
|
+
const appVersion = phoneInfo()?.appVersion ?? "unknown";
|
|
1313
|
+
this.emit(this.env.now(), (base) => ({
|
|
1314
|
+
...base,
|
|
1315
|
+
kind: "connection_event",
|
|
1316
|
+
...(binding ? { deviceBinding: binding } : {}),
|
|
1317
|
+
payload: {
|
|
1318
|
+
event: event.event,
|
|
1319
|
+
category: event.category,
|
|
1320
|
+
...(glassesModel ? { glassesModel } : {}),
|
|
1321
|
+
appVersion,
|
|
1322
|
+
...(event.event === "reconnected" ? { reconnectMs: Math.max(0, Math.round(event.reconnectMs ?? 0)) } : {}),
|
|
1182
1323
|
},
|
|
1183
1324
|
}));
|
|
1184
1325
|
}
|
|
@@ -1230,10 +1371,10 @@ class CollectorRun {
|
|
|
1230
1371
|
return measured.filter((capability) => capability !== "phone_battery");
|
|
1231
1372
|
return [...measured];
|
|
1232
1373
|
}
|
|
1233
|
-
base(observedAt) {
|
|
1374
|
+
base(observedAt, idPrefix = "") {
|
|
1234
1375
|
return {
|
|
1235
1376
|
schemaVersion: FLEET_SCHEMA_VERSION,
|
|
1236
|
-
recordId: newId()
|
|
1377
|
+
recordId: `${idPrefix}${newId()}`,
|
|
1237
1378
|
phoneDeviceId: this.phoneId,
|
|
1238
1379
|
collectorSessionId: this.collectorSessionId,
|
|
1239
1380
|
sequence: this.env.queue.nextSequence(this.partition, this.collectorSessionId),
|
|
@@ -1242,9 +1383,9 @@ class CollectorRun {
|
|
|
1242
1383
|
};
|
|
1243
1384
|
}
|
|
1244
1385
|
/** Builds a record and queues it in this run's partition. A record that cannot be built or stored is skipped. */
|
|
1245
|
-
emit(observedAt, make) {
|
|
1386
|
+
emit(observedAt, make, idPrefix = "") {
|
|
1246
1387
|
try {
|
|
1247
|
-
this.env.queue.enqueue(this.partition, make(this.base(observedAt)));
|
|
1388
|
+
this.env.queue.enqueue(this.partition, make(this.base(observedAt, idPrefix)));
|
|
1248
1389
|
}
|
|
1249
1390
|
catch (error) {
|
|
1250
1391
|
warn("recording", error);
|