CreaRack-SL

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_fetches explí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ímitePlan FreePlan PaidMitigación
Subrequests por invocación501000Bulk queries + db.batch()
Bind params por query D1~100~100IN chunked ≤90
LIKE sobre JSON serializado⚠️ inestable⚠️ inestableParsear en memoria JS
Tiempo CPU por invocación10 ms30 s—
Memoria por invocación128 MB128 MB—

Historial de incidencias en CreaRack Pro

FechaFunción afectadaErrorFix
2026-04-22wikiLintBulk (sesión 10)Too many subrequests + LIKE too complexcommit 2319b6c — bulk query + reverse-index + IN chunked

Trabajo pendiente

  • wikiLintContradictions: misma revisión pendiente (sesión 11). Ver entity--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