Footguns CF Workers + D1: límites de subrequests y bind params
Footguns CF Workers + D1: límites de subrequests y bind params
Guía de referencia sobre los dos límites del runtime que han causado fallos reales en CreaRack Pro, con patrones de mitigación probados.
1. Límite de subrequests — CF Workers Free (50 / invocación)
El problema
CF Workers Free limita a 50 subrequests por invocación (fetch + D1 queries cuentan). Workers Paid eleva el límite a 1000, pero el plan Free es el objetivo operativo del proyecto.
Cualquier función que haga una query D1 por elemento de una lista (for (const item of list) { await db.prepare(...).run() }) explota este límite con listas ≥50 elementos.
Error observable:
Too many subrequests.
Patrón seguro: bulk query + procesamiento en memoria
// ❌ MALO — N subrequests
for (const page of pages) {
const refs = await db.prepare('SELECT ... WHERE slug = ?').bind(page.slug).first();
// ...
}
// ✅ BUENO — 1 subrequest
const allPages = (await db.prepare('SELECT ... FROM bib_wiki_pages WHERE status = ?')
.bind('active').all()).results ?? [];
// Procesamiento en memoria JS
const index = new Map(allPages.map(p => [p.slug, p]));
Patrón seguro: db.batch() para escrituras bulk
// ❌ MALO — N subrequests
for (const s of staleList) {
await db.prepare('UPDATE ... WHERE slug = ?').bind(s.slug).run();
}
// ✅ BUENO — 1 subrequest
const statements = staleList.map(s =>
db.prepare('UPDATE bib_wiki_pages SET status = ? WHERE slug = ?').bind('stale', s.slug)
);
await db.batch(statements);
2. Límite de bind params — D1 (~100 por query)
El problema
D1 tiene un límite no documentado formalmente de ~100 bind params por query. Usar IN (?, ?, ...) con listas largas supera este límite.
Error observable:
D1_ERROR: too many bind parameters
Patrón seguro: IN chunked a ≤90
const ids = items.map(i => i.id); // puede ser > 100 elementos
for (let i = 0; i < ids.length; i += 90) {
const chunk = ids.slice(i, i + 90);
const placeholders = chunk.map(() => '?').join(',');
const rows = await db
.prepare(`SELECT * FROM table WHERE id IN (${placeholders})`)
.bind(...chunk)
.all();
// procesar rows...
}
¿Por qué 90 y no 100? Margen de seguridad. Algunos drivers cuentan params de forma diferente según contexto.
3. “LIKE pattern too complex” — D1
El problema
D1 (SQLite subyacente) puede rechazar patrones LIKE con ciertos caracteres especiales o patrones suficientemente complejos cuando se combinan con columnas JSON serializado.
Caso concreto: buscar backlinks con WHERE related LIKE '%"slug-largo-con-guiones"%' falla intermitentemente.
Error observable:
D1_ERROR: LIKE pattern too complex
Patrón seguro: parsear JSON en memoria
// ❌ MALO — LIKE sobre JSON serializado
const refs = await db
.prepare(`SELECT COUNT(*) FROM bib_wiki_pages WHERE related LIKE ?`)
.bind(`%"${slug}"%`)
.first();
// ✅ BUENO — leer todo en memoria y parsear en JS
const referenced = new Set<string>();
for (const p of allPages) {
const rel = JSON.parse(p.related || '[]') as string[];
for (const r of rel) referenced.add(r);
}
const isOrphan = !referenced.has(slug);
4. Límite de fetches externos (GitHub API, etc.)
CF Workers Free cuenta las llamadas a fetch() externas dentro del presupuesto de subrequests. Para operaciones que necesitan verificar N archivos en GitHub:
- Establecer un parámetro
max_source_fetchesexplícito. - Default OFF para operaciones de cron frecuentes (diario).
- Default ON con límite bajo solo en cron semanal.
const maxSourceFetches = args.max_source_fetches ?? 30;
let sourcesChecked = 0;
outer: for (const page of pages) {
for (const src of page.sources) {
if (sourcesChecked >= maxSourceFetches) break outer; // hard stop
sourcesChecked++;
await fetch(`https://api.github.com/...`);
}
}
Tabla de referencia rápida
| Límite | Plan Free | Plan Paid | Mitigación |
|---|---|---|---|
| Subrequests por invocación | 50 | 1000 | Bulk queries + db.batch() |
| Bind params por query D1 | ~100 | ~100 | IN chunked ≤90 |
| LIKE sobre JSON serializado | ⚠️ inestable | ⚠️ inestable | Parsear en memoria JS |
| Tiempo CPU por invocación | 10 ms | 30 s | — |
| Memoria por invocación | 128 MB | 128 MB | — |
Historial de incidencias en CreaRack Pro
| Fecha | Función afectada | Error | Fix |
|---|---|---|---|
| 2026-04-22 | wikiLintBulk (sesión 10) | Too many subrequests + LIKE too complex | commit 2319b6c — bulk query + reverse-index + IN chunked |
Trabajo pendiente
wikiLintContradictions: misma revisión pendiente (sesión 11). Verentity--mcp--tool--wiki-lint-bulk.
Véase también
- [[entity—mcp—tool—wiki-lint-bulk]]
- [[incident—20260315—cf-env-vars-next-deploy]] — CF Pages env vars no aplican sin re-deploy
- [[incident—20260421—d1-bind-overflow-silent]] — D1 bind-param overflow silencioso
- [[runbook—infra—rotate-mcp-token]] — runbook rotación BIB_MCP_TOKEN en todos los entornos