Tech check / Senior review / 01_prep_map.md

Senior review — Python backend / AI engineer — prep map

Updated 28 min read source
On this page12
  1. 🔴 Step 1 — Web & Networking
  2. 🔴 Step 2 — Auth
  3. 🔴 Step 3 — Python language features
  4. 🔴 Step 4 — Python memory and internals
  5. 🔴 Step 5 — Concurrency and async
  6. 🔴 Step 6 — Databases
  7. 🟡 Step 7 — Frameworks and backend architecture
  8. 🟡 Step 8 — Docker
  9. 🟡 Step 9 — Cloud (AWS) and monitoring
  10. 🔴 Step 10 — System design
  11. 🟡 Step 11 — AI
  12. 🟢 Step 12 — Other domains

Senior review — Python backend / AI engineer — prep map

Every theme from the technical-review feedback, brought to senior+ depth one article at a time (October 2026). Read top to bottom; tick an item once you can explain and defend it under questioning, not merely when you have read it. Bold is the article written or rewritten for this review; the rest add depth. Items were reviewed and approved between 2026-10-09 and 2026-10-15 (Elasticsearch, multi-agent systems and RAG at scale were added on the 15th); the summaries name what each article demonstrates, including the measured runs.

Priority: 🔴 core (asked, or flagged for depth) · 🟡 important · 🟢 breadth


🔴 Step 1 — Web & Networking

🔴 Step 2 — Auth

🔴 Step 3 — Python language features

🔴 Step 4 — Python memory and internals

  • Stack and heap memory — every object on the heap, frames hold references; where frames live (3.11 data stack, 3.12 no C stack for Python calls); the two recursion limits; why threads cost stacks and coroutines do not; what frames keep aliveStack and heap in PythonMemory modelArgument passingasyncio vs threads
  • Memory model: allocation, reference counting, garbage collection — the object header; reference counting and why getrefcount varies by version; immediate, cascading freeing and where it is deferred; immortal objects; refcounting without the GIL; finalizersPython memory modelStack and heapweakrefGIL
  • Garbage collector — how cycles are found without a root set; thresholds by version and the 3.14.0-3.14.4 incremental collector that 3.14.5 reverted; what is tracked; pause cost measured with gc.callbacks; gc.freeze() for prefork servers; leaks the collector cannot seePython memory model: the cycle collectorMemory profilingweakref
  • Memory management: heap, arenas, pools, blocks — the four allocator layers; blocks, 16 KiB pools and 1 MiB arenas (3.10+); why a process does not shrink (fragmentation, measured); mimalloc on the free-threaded build; inspecting and working around itThe allocator: arenas, pools, blocksMemory modelMemory profiling__slots__
  • Memory management in Python, deep — one object end to end; which number to read (RSS, USS, PSS, tracemalloc, cgroup); four causes measured on Linux: a leak (lru_cache on a method, found by snapshot diff), a plateau (glibc kept 201 MiB, malloc_trim → 41), per-thread arenas (MALLOC_ARENA_MAX), copy-on-write after fork (46 MiB per worker, 0 with gc.freeze()); exit 137 in containers; __slots__ 128 → 88 B; a 7-step playbookPython memory in productionMemory modelThe allocatorMemory profiling

🔴 Step 5 — Concurrency and async

🔴 Step 6 — Databases

  • SQL — NULL and three-valued logic; LEFT JOIN turned INNER by a WHERE filter, seen in the plan; GROUP BY and functional dependency; the fan-out trap in aggregates; semi- and anti-joins, NOT IN vs NOT EXISTS (0 vs 50 rows, 1.5 s vs 53 ms); normalization and when to break it; OFFSET vs keyset; logical clause orderSQL fundamentalsHow a query is processedWindow functions and CTEsCTEs and recursive queries
  • PostgreSQL — process per connection (27,122 vs 388 tps without pooling); shared_buffers and per-node work_mem; heap, MVCC tuple versions, HOT updates, TOAST; VACUUM and the xmin horizon, measured; WAL and replication; what changed in 18; vs MySQL InnoDBPostgreSQL architectureVACUUM and autovacuumWAL and checkpointsConnection pooling
  • MongoDB — the document as the unit of atomicity and the 16 MB limit; lost updates from read-modify-write (18 of 100) vs $inc; embed vs reference; $jsonSchema validation and null vs missing; COLLSCAN vs IXSCAN, measured; write concern majority and rollback on failover; transactions and their cost; PyMongo async replacing MotorMongoDBTransactions and concernsIndexesReplica sets and shardingNoSQL decision guide
  • Indexes in PostgreSQL: types in detail, pros and cons — the capability matrix from the server; six access methods with measured size, build and insert cost; B-tree surprises (collation and LIKE, 2,704-byte cap, PG18 skip scan); hash only wins on long keys; GIN operator matching, jsonb_path_ops, the pending list; GiST exclusion constraints; SP-GiST; BRIN and correlation (266 vs 57,201 buffers)Index typesHow a B-tree index worksWhy your index is not usedpgvector in production
  • EXPLAIN / EXPLAIN ANALYZE — inside-out reading, loops multiply, time is inclusive (a real 1-vs-49,975 misestimate: 150,466 vs 1,082 buffers after ANALYZE); join strategies and hash batches; spills, lossy bitmaps, Memoize, workers not launched, JIT cost; fast in EXPLAIN but slow in the app: generic plans, SERIALIZE, the client; auto_explain; no hints, and the escape hatchesReading a query planHow a query is processedWhy your index is not usedIndex types
  • pg_stat_statements — setup and how queries are grouped (constants to $1, PG18 IN-list squashing, one text split by parameter types); rank by total, not mean (a 1 ms update cost 4.6 s); six rankings; blind spots: N+1 is 21 ms in the DB and 1.1 s in the client, cancelled statements leave no entry; cumulative counters and deltas; logs and traces as the complementpg_stat_statementsReading a query planN+1Incident response and runbooks
  • ACID — how each letter is delivered: rollback as a status bit (1M rows rolled back in 0.2 ms, cost moved to VACUUM), the aborted state after an error vs MySQL, savepoint costs; per-row vs deferred constraints and cross-row invariants; isolation interleaves; durability as a setting (synchronous_commit off: 3× TPS, 600 ms at risk), failover loss; implicit transactions in psycopgTransactions and ACIDWAL and checkpointsPostgreSQL architectureSQLAlchemy transactionsCAP, ACID and BASE
  • Transaction isolation levels — snapshots and when they are taken (first statement, not BEGIN); READ COMMITTED: read skew (250 vs 200), silent lost update, the re-check after a wait; REPEATABLE READ: first-updater-wins 40001, write skew; SERIALIZABLE via SSI and SIRead locks, retries by SQLSTATE; FOR UPDATE on a missing row double-books; row lock modes; retry storms, long reports, deadlocksTransaction isolation levelsTransactions and ACIDAdvisory locksSQLAlchemy transactions
  • Concurrent transaction anomalies — the anomalies by name (P0-P4, A5A/A5B, Adya’s G-codes); a PostgreSQL vs InnoDB matrix, all run; InnoDB’s snapshot vs current reads (100, 100, then 160), silent lost updates, phantoms hidden from reads but not writes, gap locks and deadlocks; SQLite’s SQLITE_BUSY_SNAPSHOT; Oracle and SQL Server; porting traps; optimistic vs pessimistic SERIALIZABLEIsolation anomalies across databasesTransaction isolation levelsMySQLSQLite
  • N+1 problem: select_related, prefetch_related — counted on Django 6.1 (101 → 1 and 2); INNER vs LEFT OUTER JOIN; what keeps a prefetch (.count()) and what discards it (.filter(): 102); Prefetch(to_attr=); when the JOIN ships 10× more data; 6.1 fetch modes (FETCH_PEERS fixes forward FKs, not reverse/M2M; FETCH_RAISE); only(), huge prefetches, async; detection in tests, APM and pg_stat_statementsN+1 in DjangoN+1 across ORMsSQLAlchemy loading strategiesDRF performance
  • SQL injection — the attack run (login bypass, UNION extraction); what the driver sends: psycopg 3 binds server-side, psycopg2 escapes client-side (CVE-2025-1094); stacked statements; what cannot be a parameter and sql.Identifier; t-strings in psycopg 3.3; ORM escape hatches and Django’s user-controlled-keys CVEs; LIKE wildcards; second-order; MongoDB operator injection; least privilege and detectionSQL injectionOWASP Top 10Python 3.14 featuresMongoDB
  • Elasticsearch — a derived, eventually consistent index of shards; hash(routing) % primaries fixes the primary count; write path (translog, refresh, flush, merge) and near-real-time search (get sees it, search does not; wait_for blocks to the next refresh); query-then-fetch and the 10,000 window; BM25 statistics per shard (4.169 on one shard, 2.471 on five); if_seq_no and external versions for sync from Postgres, deletes forgotten after gc_deletes; shard and node sizing, disk watermarks; Postgres full-text firstElasticsearch overviewQuery DSL and scoringPython client and pitfallsTransactional outbox

🟡 Step 7 — Frameworks and backend architecture

  • FastAPI Depends: the dependency graph — how the tree is built and classified; resolution order (app, router, decorator, params) and sequential awaiting (2 × 100 ms = 0.22 s); the cache key (callable + security scopes + teardown scope); teardown after the response since 0.118, so a failing commit returns 201 (500 with scope="function"); the version history; overridesDependencies and injectionFastAPI and the event loopSecurity and authenticationDependency injection
  • Django and Django REST Framework — the request path traced (negotiation, auth, permissions, throttles, handler, object permissions only in get_object()); the list leak; 401 vs 403 from the first auth class; atomic does not stop lost updates (90 vs 50; select_for_update, F()); on_commit; async limits in Django 6.1; serializer cost vs values()Django and DRF: the request pathPermissionsAsync DjangoN+1 in Django
  • Monolithic backend applications — one artifact, not one process; a boundary measured (59 ns vs 0.9 ms over HTTP, JSON cost); availability and p99 across synchronous chains; scaling a monolith per Deployment; Conway’s law and deploy-contention metrics; a modular monolith enforced by import-linter and per-module schemas; extraction order; distributed monolith, retry storms, Prime Video and SegmentMonolith vs microservicesData consistency patternsTransactional outboxFeature-sliced structure
  • Service layer vs classic MVC in Django — which entry points run save(), clean() and signals (forms only for clean(); update() and bulk_create() skip all); invariants as database constraints; service functions owning the transaction, select_for_update, on_commit (an email for a rolled-back order); selectors returning QuerySets (51 vs 1 queries); the placement table; repositories and hexagonal as tradesService layer vs fat modelsDjango and DRFRepository and Unit of WorkClean architecture
  • Celery and background job processing — when a task is acknowledged: with acks_late a failure is acked, an OOM-killed child is lost unless task_reject_on_worker_lost; the Redis visibility-timeout duplicate, which appears when a worker starts (deploys); every cause of running twice or never; prefetch (6 reserved + 2 active); runtime guards; result.get() in a task; monitoring queue ageWhat is CeleryIdempotencyDead-letter queueAsync I/O and background work
  • Redis — one command thread, I/O threads off by default; what blocks everyone (KEYS stalled PING 249 ms, DEL 179 ms vs UNLINK 0.3 ms); pipelining 25×; encodings and the 513-field jump (4.5 → 22 KB); Redis 8 built-ins and the AGPL change; locks on failover, fork memory, big and hot keysWhat is RedisPersistence and memoryClustering and HARedlock critiqueValkey
  • Idempotency keys and “at most once” delivery — two concurrent requests, one key, run on PostgreSQL 18: FOR UPDATE gives a 500, a committed claim gives 409 then replay, a claim inside the request transaction blocks and double-charges after a crash; atomic phases and derived provider keys; which 4xx to retry; Stripe replay and expiry; delivery semantics per hop and when at-most-once is rightIdempotency keysIdempotent paymentsKafka delivery semanticsTransactional outbox
  • Dependency inversion vs dependency injection — principle vs technique: which way source dependencies point vs who wires objects; ports owned by the domain; Protocol checked by mypy (and runtime_checkable passing a broken adapter); DI without DIP caught by an import-linter layers contract; DIP without DI (service locator); composition root, Depends, fixtures, containersDependency inversion vs injectionSOLIDDependency injectionFastAPI Depends
  • *DDD, TDD, BDD and the other -driven approaches — design approaches vs ways of working; the map by what comes first; TDD and BDD in one red-green run (pytest-bdd); a DDD aggregate guarding an invariant; where each goes wrong (testing mocks, unread Gherkin, DDD on CRUD, untraceable events); risk-driven design as the answer to “how much”X-driven: the mapDomain-driven designBDD and specification by exampleTest pyramid and strategy

🟡 Step 8 — Docker

  • Images vs containers — layers, config and digests (tags move); a container as a host process with namespaces, cgroups and a writable layer; docker diff, copy-up of a whole 5.6 MB file for one byte, data gone on rm; volumes vs bind mounts vs tmpfs; limits the runtime cannot see; PID 1 ignoring SIGTERM (137) vs --init or a handlerDocker image vs containerImage layersScaling in DockerPython memory in production
  • Docker Compose — depends_on orders start, not readiness (3 of 3 connection refused); healthcheck + service_healthy, a migrations gate with service_completed_successfully; restarts later need app retries; override files append lists (!override); Postgres 18’s new volume path; obsolete version, CLI v5; secretsDocker ComposeVolume typesScaling in DockerImage vs container
  • Layers and build caching — cache keys: COPY by content (touch stays cached), RUN by command text (RUN date cached forever); config instructions are 0 B; order dependencies before source; cache mounts (8 downloads vs 1); deleted files and secrets stay in layers; a tested uv multi-stage build (--no-editable, --locked not --frozen); mid-line comments break buildsLayers and build cachingMulti-stage buildsVirtualenvs and lockfilesImage vs container
  • ADD vs COPY — ADD extracts local tars (not zips), downloads URLs (mode 0600) and clones Git; --checksum fails on changed content where RUN curl stays cached; COPY optionsADD vs COPYLayers and build cachingMulti-stage builds
  • Multi-arch builds — the image index (8 platforms, a digest each; pin the index); QEMU emulation 33× slower on CPU, 13× on wheel installs; cross-platform stages on $BUILDPLATFORM (204.6 s → 19.5 s); native runners and imagetools create; exec format error, hard-coded FROM --platform, attestations, registry cleanup; Graviton economicsMulti-arch buildsLayers and build cachingImage vs container

🟡 Step 9 — Cloud (AWS) and monitoring

  • ECS, Fargate — task vs execution role and the IMDS trap on EC2; a deployment stuck half rolled out (a curl health check in an image without curl, grace periods, circuit breaker); scaling workers on backlog per task, scaling to zero; built-in blue/green with hooks (2025); awslogs non-blocking default drops logs; Service Connect; Fargate cold starts (no image cache, SOCI); App Runner closed, ECS Express ModeAmazon ECSAWS FargateImage vs container
  • CloudWatch — what EC2 does not report (memory, disk space) and real resolutions; retention roll-ups; custom metrics and EMF, tiered pricing (100k metrics $12k/month, 1M $64.5k) and high-cardinality dimensions; log ingestion costs and Logs Insights per GB scanned; alarm evaluation (M of N, missing data and the alarm that never fires, rates and percentiles); X-Ray SDK end of support → OpenTelemetryAmazon CloudWatchCost and advanced patternsThree pillars
  • Cognito — user pool vs identity pool; ID vs access token (no aud on access tokens), the seven backend checks; ALB authenticate-cognito end to end (signed x-amzn-oidc-data, signer, ALB-only security group, 7-day session, two-step logout); sign-out vs offline validation, rotation; pre-token V1/V2/V3; plans, M2M per-token pricing, quotas, multi-region (2026); identity-pool S3 prefixesAmazon CognitoCognito for Python backendsOAuth 2 and OIDCJWT pitfalls
  • Grafana — a panel as server-side queries, $__interval and dashboard load maths; $__rate_interval and the scrape-interval setting, Prometheus 3 left-open ranges; histogram quantiles; alert states (Recovering, No Data), the dead exporter that never pages, routing defaults, where rules live; HA (ha_peers, shared DB); provisioning semantics, Git Sync; Alloy, Loki streams and structured metadata, exemplarsGrafanaPrometheusSLO, SLI, SLACorrelation IDs
  • Observability — what metrics, logs, traces and profiles store and cost; traceparent, trace ids in logs, exemplars, stable semconv renames; p99 490 ms vs 2 s timeouts (the hidden 0.3%, bucket interpolation 0.298 → 0.486, averaged p99s, failures the server never sees); who monitors the pipeline (span-queue drops, Collector counters); a worked cost for 50 services; SDK vs eBPF; wide eventsThree pillarsOpenTelemetryGrafanaSampling strategiesSLO, SLI, SLA

🔴 Step 10 — System design

  • Core of system design — eight phases on a clock (diagram by minute 25); NFRs as mechanisms (99.99% needs multi-AZ failover, RPO = 0 costs a cross-AZ RTT per commit, RTO as a sum); Little’s law (120 in flight vs max_connections = 100, PgBouncer), backlog drain, tail at scale (63% at N=100), cost as a number; a data model checked against access patterns; the failure walk (cache at 0% = 100x DB reads); open-model load tests; a worked 45-minute webhook transcriptThe frameworkCapacity estimationLatency numbersTimeouts and retriesWorked designs
  • Distributed systems — partial failure (a timeout is the only detector; two generals, FLP); wall vs monotonic clocks, Lamport (a receive stamped before its send), vector, HLC, TrueTime; majorities and odd cluster sizes; Raft (terms, votes, commit, ReadIndex); a paused lease holder overwriting its successor, stopped by a fencing token; etcd, KRaft, Kubernetes Leases, Patroni, Sentinel; advisory-lock leaders (2 h 11 min on keepalive defaults), the epoch rowConsensus and leader electionReplication and consistencyRedlock critiqueAdvisory locks
  • Microservices — running them once split: readiness that empties a Service, deadline propagation, retries multiplying (27x at 3 tiers) and chain availability; identity across hops (spoofable X-User-Id, forwarded or exchanged tokens, mTLS); outbox and idempotent consumers; expand/contract under version skew, Pact, schema registry; 502s on deploy and the preStop sleep; traces through queues; gateway vs meshMicroservices in productionMonolith vs microservicesData consistency patternsResilience patterns
  • Caching — four patterns and where each goes stale (write-through is not “never stale”); the read/write race delete-on-write leaves open (run: cache=old, db=new) and the replica-lag variant; leases (run: late SET rejected), versioned Lua CAS, double delete, CDC, TTL as the bound; negative caching, hit-ratio arithmetic, a stale-read rate; in-process vs RESP3 client-side caching vs CDN; versioned key prefixes on deployCache consistencyCache stampedeRedis persistence and memoryDistributed cache design
  • Design: LLM usage statistics (tokens, model, latency; ACID, observability, caching, idempotency) — one gateway as the capture point; provider usage traps (input_tokens excludes cache, cache write 1.25x/2x and read 0.1x/0.05x, cumulative stream counts) and a stream cut off mid-answer; integer micro-dollars and a versioned price table; dedup by event id vs retries as real calls; budgets by reserve-then-settle in one conditional UPDATE; hourly reconciliation with the provider’s usage API; TTFT and cardinality; three meanings of cachingLLM usage meteringIdempotent paymentsThe frameworkThree pillars
  • Design: image/file processing app (storage, virus protection, async workflow, not blocking users) — uploads straight to S3 by pre-signed POST (size enforced by the policy); a state machine with conditional transitions and status polling; quarantine, GuardDuty for S3 tags (only NO_THREATS_FOUND is clean) and a deny-unless-clean bucket policy; hostile-file processing (magic bytes, pixel caps, re-encode, EXIF GPS); content-hash output keys, SQS visibility timeout and DLQ; user content on a separate domain; storage as the cost lineFile processing pipelineJob schedulerXSS and CSRFThe framework

🟡 Step 11 — AI

  • OpenAI API — Responses (primary) vs Chat Completions (portable), Assistants shut down 2026-08-26; previous_response_id still bills every prior token, store=False; strict structured outputs and refusals; the Responses tool loop and the silent non-strict fallback; named stream events vs the empty-choices usage chunk; input_tokens includes cache, reasoning counts against max_output_tokens, paid cache writes; Batch/Flex; 429s at 40% TPM, insufficient_quota, 12 nested retries, no idempotency keyThe OpenAI API in practiceThe Claude Messages APIThe chat APIFunction calling and structured output
  • SSE as the streaming protocol for LLM responses — re-stream the provider inside the response generator (runnable fake provider and fastapi.sse backend); one event schema of your own, errors in-band, never bill from stream usage; Stop measured one hop further (7 delivered, 8 generated); the detached pump that billed 33 tokens for 7 seen; coupled vs decoupled runs and a grace-period cancel; token sizes leaking answers (Weiss et al. 27%, Whisper Leak)Streaming LLM responsesServer-Sent EventsThe OpenAI APILLM usage metering
  • LangChain, LangGraph and the modern agent stack — LangGraph 1.2.15 is the runtime (there is no 1.3), LangChain 1.4.5 is create_agent + middleware; supersteps over versioned channels, reducers (InvalidUpdateError), pending writes, durability modes; interrupts re-run the node (run: side effect twice) and except Exception swallows the pause; Postgres checkpointer ops, no TTL, delete_thread, the serializer as a code-execution boundary; thread_id as a capability, double texting, recursion limits of 10,007/9,999; choosing among own loop + MCP, LangGraph, OpenAI Agents SDK, Pydantic AI, Claude Agent SDKLangChain, LangGraph and the agent stackLangGraph in depthThe agent loopDurable execution and HITL
  • Multi-agent systems — usually the wrong first answer; sub-agent as a tool, isolated context; supervisor, pipeline, parallel with reducer; 0.9^5 = 0.59 reliability; typed handoff with gaps; Anthropic +90.2% at about 15x chat tokens vs Cognition’s shared-context rule; one budget plus TaskGroup cancellation (run: runaway stops its siblings); inter-agent injection, conflicting parallel writes; trace the tree, keep a single-agent baselineMulti-agent patternsMulti-agent frameworksLangGraph multi-agent and subgraphsAgent protocols
  • RAG on large data volumes — size it in chunks (200M x 1024 dims: 820 GB float32, 26 GB binary; 80B embedding tokens is cheap in dollars, days in wall-clock); quality drops as the corpus grows (run: exact top-1 0.97 at 10k, 0.57 at 1M, 0.98 filtered to one source), so filter, route, dedupe, retrieve documents first; int8 / binary with rescoring, Matryoshka, DiskANN, and the task metric vs overlap; shard by key, fan-out tail latency, every shard returns k, partial flag; bulk load then build, resumable embed workers, re-embed needs two indexesRAG at scaleANN indexes and HNSWHybrid search and rerankingIngestion and index sync

🟢 Step 12 — Other domains

  • Blockchain — what a chain guarantees to a backend; Merkle proofs (20 hashes for 1M transactions, run); finality per chain (Bitcoin ~6 blocks, Ethereum finalized ~13 min, rollups); UTXO vs account nonces, stuck nonces and fee bumps; EIP-1559 fees, integer base units; crediting deposits exactly once under a reorg (simulated: duplicates, a vanished deposit, confirmations shallower than the reorg); KMS/HSM signing, MPC vs multisig; stablecoin freezes, OFAC, Travel Rule, MiCA, GENIUS ActBlockchain fundamentalsIdempotency keysTransactional outboxConsensus and leader election
  • Web scraping — API first, then the site’s own JSON, HTML, a browser last; client defaults that bite (no timeout, no redirects, UTF-8 fallback); RFC 9309 robots.txt and Python’s parser getting it backwards before 3.14 (run on 3.12/3.13/3.14); a per-host bounded fetch loop with Retry-After; conditional GET, content hashes, idempotent upserts, WARC; Playwright cost; anti-bot layers and soft 200 blocks; the legal landmarks (Van Buren, hiQ, Meta v. Bright Data, GDPR)Web scraping and browser automationTimeouts, retries, poolingSSRFIngestion and index sync
  • Fintech — money as integer minor units (int(19.99 * 100) is 1998; sum() is compensated since 3.12), ISO 4217 decimals, banker’s rounding; largest-remainder allocation with a property test; journals and postings balanced by a deferred constraint trigger, idempotent by UNIQUE (source, external_id); overdraws under READ COMMITTED and three fixes, hot accounts; reversing journals, hash chains, WORM; three-way reconciliation and breaksDouble-entry ledgerIdempotent paymentsIsolation anomaliesBlockchain fundamentals
  • Frontend development — streaming an LLM answer into React: EventSource semantics (fixed retry:, a 401 closes it forever, GET only); a blank-line-only parser that emits nothing against CRLF servers (run in Node) vs a spec-shaped one; an abortable hook that batches tokens per animation frame, aborts on unmount, handles in-band error and flags missing done as truncated; what Stop does down to the provider; quadratic markdown re-renders; cancel vs resumeStreaming an LLM answer into ReactStreaming LLM responses (backend)Server-Sent EventsAbort and race conditions