GEMINI LABJP
CLI — While the Gemini API release notes have sat still since September 3, the CLI moved: v0.59.0 is now stable, and the changes are mostly about securitySSRF — Server-side request forgery in MCP OAuth metadata discovery has been closed off. If you connect third-party MCP servers, this one is for youRESTRICTED — Restricted mode now enforces fail-closed workspace trust and filters MCP servers. Expect different behaviour if you run unattended with MCP attachedPINNING — Explicitly versioned Flash model IDs were not being preserved. If you pin versions for reproducibility, this quietly affected youSEP 30 — Seventeen days until gemini-omni-flash-preview is retired, and gemini-2.5-flash-image follows on October 2MIGRATION — The official table still points to gemini-3.1-flash-image-preview, which was retired on June 25. The real destination is the GA gemini-3.1-flash-imageCLI — While the Gemini API release notes have sat still since September 3, the CLI moved: v0.59.0 is now stable, and the changes are mostly about securitySSRF — Server-side request forgery in MCP OAuth metadata discovery has been closed off. If you connect third-party MCP servers, this one is for youRESTRICTED — Restricted mode now enforces fail-closed workspace trust and filters MCP servers. Expect different behaviour if you run unattended with MCP attachedPINNING — Explicitly versioned Flash model IDs were not being preserved. If you pin versions for reproducibility, this quietly affected youSEP 30 — Seventeen days until gemini-omni-flash-preview is retired, and gemini-2.5-flash-image follows on October 2MIGRATION — The official table still points to gemini-3.1-flash-image-preview, which was retired on June 25. The real destination is the GA gemini-3.1-flash-image
Articles/API / SDK
API / SDK/2026-06-13Advanced

Rebuilding a Three-Layer RAG Cache After Migrating to Gemini 3.5 Flash

When Gemini 2.0 Flash was retired, I rebuilt my RAG caching stack around 3.5 Flash. Here are the working implementations for response, semantic, and embedding caches, measured hit rates from production, and how self-managed caching divides the work with the API's Context Caching.

gemini-3-5-flash3rag22cost-optimization31caching2gemini-embedding-27

Premium Article

When Gemini 2.0 Flash was retired on June 1, my RAG pipeline — an indie developer project running on a solo budget — moved to 3.5 Flash. I would have loved for the migration to end with swapping a model ID and running the test suite. In practice, that was where the real work started: once pricing and token-handling assumptions change, a cache design that used to be optimal quietly stops being so.

So while I was in there, I peeled off every cache layer and rebuilt the stack from scratch. The punchline: I ended up with the same three-layer structure as before. What changed was the priority of each layer and how the work is divided with the API's own Context Caching. This article walks through the rebuilt design with working code and production numbers.

Cut your caches along the cost boundaries

A single RAG request spends money in exactly three places.

  1. Query embedding — the API call that vectorizes the question
  2. Vector search — the database query (billed per search on managed services)
  3. Answer generation — sending context plus question to the model

Items 2 and 3 dominate the invoice. That is why I find it cleanest to organize caches by which cost point each one eliminates:

  • L1 (response cache): stores answers to identical questions; a hit skips all three steps
  • L2 (semantic cache): reuses retrieval results for semantically similar past queries; skips step 2
  • L3 (embedding cache): never recomputes the embedding of an identical string; skips step 1

The higher the layer, the bigger the saving on a hit. That hierarchy survived the migration intact. What shifted was my judgment about how much to keep self-managed — more on that below.

L1: the response cache is still the first thing to install

User questions repeat far more than you expect. In FAQ-shaped workloads, exact matches alone catch around a third of traffic. In my production data for the past week, L1 hit 34% — one in three generation calls evaporates before it ever reaches the API.

import hashlib
import json
import redis
 
from google import genai
 
client = genai.Client()  # reads GEMINI_API_KEY from the environment
r = redis.Redis(decode_responses=True)
RESP_TTL = 60 * 60 * 24 * 7  # 7 days
 
def response_cache_key(tenant: str, query: str, filters: dict) -> str:
    """Build a deterministic key from tenant, normalized query, and filters."""
    payload = json.dumps(
        {"t": tenant, "q": " ".join(query.lower().split()), "f": filters},
        sort_keys=True,
    )
    return "rag:l1:" + hashlib.sha256(payload.encode()).hexdigest()
 
def answer_with_l1(tenant: str, query: str, filters: dict) -> dict:
    key = response_cache_key(tenant, query, filters)
    if (hit := r.get(key)) is not None:
        return json.loads(hit)
    result = run_rag_pipeline(tenant, query, filters)  # includes L2/L3
    r.setex(key, RESP_TTL, json.dumps(result))
    return result

Three deliberate choices here. First, normalization via lower().split() collapses case and whitespace variants into one key. Second, the tenant ID goes into the key, always — I once came within a code review of shipping a version without it, which would have served one tenant's answers to another. Permission boundaries belong in the cache key itself. Third, the TTL: keep serving yesterday's answer after a document update and user trust erodes quietly.

For invalidation I don't rely on TTL alone. Each cached answer carries the IDs of the documents it cites, and a document-update event purges every answer referencing it. At weekly update cadence, that mechanism alone has kept stale-answer incidents at zero.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Working three-layer cache code (response, semantic, embedding) built on the google-genai SDK and Redis
The verification process behind a 0.92 similarity threshold, plus invalidation design against false hits, tenant bleed, and stale answers
How to divide responsibilities between the API's Context Caching and your own layers, with per-layer hit-rate instrumentation and an investment rule for L2
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

API / SDK2026-07-06
Ingested but Never Cited: Pruning a File Search Store with Citation Logs and a Quarantine Window
Most documents in a File Search store are never cited, quietly draining both cost and retrieval quality. Learn to log grounding metadata, surface never-cited documents from real usage data, and prune them safely with a quarantine window — with working code.
API / SDK2026-06-23
Your File Search Store Goes Stale in Production — Catalog Sync and Drift Detection That Actually Hold
Load a catalog into File Search once and forget it, and within weeks it starts confidently pointing users at assets you already pulled. Here is the sync pipeline I run: hash-based incremental import, a blue/green rebuild that swallows deletions, and a nightly drift audit.
API / SDK2026-04-19
Gemini API Caching in Production — Operational Notes from an Indie Mobile Developer
Field notes on running Gemini API's Context Caching and Implicit Caching together inside indie mobile apps. Includes working Python code, six months of measured costs from AdMob-funded apps, and seven non-obvious operational pitfalls.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links