Skip to Content
Kido Bag
Domain Combo SuitesPillar 3. Frontend CWV & Telemetry

@kido/sitemap-incremental

Cold/Hot Incremental XML Sitemap Generator, Atomic Disk Writes, Level-9 Pre-Gzip Compression, and Sub-1ms Nginx Static Serving.

@kido/sitemap-incremental

Architecture Paradigm: Cold/Hot Archive Partitioning + Atomic .tmp Safe Writes + Pre-Compressed Level-9 Gzip + Zero-Copy Kernel sendfile Serving.


💥 The Real-World Bottleneck (Why It Exists)

In high-volume content, media, and e-commerce websites with 100,000 to 10,000,000 URLs:

  1. The 504 Gateway Timeout Crawler Crash:
    • Web frameworks (Next.js App Router, Nuxt, Remix) frequently generate XML sitemaps dynamically on-demand inside SSR route handlers (app/sitemap.ts).
    • When Googlebot, Bingbot, and Yandex spider simultaneously, hundreds of concurrent sitemap requests execute heavy database table scans, serialize massive XML strings, and push CPU to 100%.
    • Result: Node.js event loops freeze, database connection pools are exhausted, and crawlers receive HTTP 504 Gateway Timeouts, causing immediate SEO ranking penalties.
  2. The 95% Redundant Computation Waste:
    • In any mature website, over 95% of articles, historical blog posts, or past catalog items are immutable.
    • Re-querying and re-serializing 500 closed monthly sitemaps every night wastes millions of database read IOPS.
  3. Corrupted Incomplete XML Reads:
    • Writing directly to destination XML files while crawlers are fetching them results in bots parsing truncated XML files, reporting false XML parse errors in Google Search Console.

🏛️ 3-Tier Production Evolution Ladder

Feature / MetricTier 1: SME BaselineTier 2: Growth / ScaleTier 3: Big-Tech Production Standard
Generation TriggerOn-demand dynamic SSR route handlerNightly cron regenerating all sitemap XMLsCold/Hot Incremental delta generation
Historical Data ProcessingRe-queries all 100k rows on every crawlFull re-scan of entire database nightlyCold weeks skipped 100% (0 DB queries)
File Write SafetyDirect stream to destination diskBasic in-memory string bufferingAtomic .tmp write + atomic OS rename
Web Server DeliveryDynamic Node.js SSR runtime (~450ms TTFB)Static Nginx serving (.xml only)Pre-gzipped .xml.gz + Nginx sendfile (< 1ms)
Google News 48h SpecNot implemented or manual routeCustom cron scriptAutomated 48h rolling Google News feed

📐 Architecture & Data Flow

Architecture BlueprintMermaid
Compiling architecture diagram...

📦 Architecture & Core API Exports

All capabilities are strictly isolated in packages/sitemap-incremental:

1. src/index.ts

  • IncrementalSitemapEngine: Master orchestrator coordinating partitioning, database adapter fetching, XML building, and disk writing.
  • buildUrlSetXml(urls, options): Formats standard <urlset> XML conforming to sitemaps.org schemas, with multi-locale xhtml:link alternates.
  • buildSitemapIndexXml(sitemaps): Builds root <sitemapindex> listing all weekly and thematic child sitemaps.
  • escapeXml(string): High-performance XML character escaping (&, <, >, ", ').
  • PostgresSourceAdapter: Database adapter querying delta records based on updated_at timestamps.
  • AtomicWriter: Disk writer handling .tmp file buffering and atomic rename operations.

💻 Production Implementation & Code Snippet

1. Incremental Sitemap Generator Job (scripts/generate-sitemaps.ts)

import {
  IncrementalSitemapEngine,
  buildUrlSetXml,
  buildSitemapIndexXml,
} from '@kido/sitemap-incremental';
import { Pool } from 'pg';

const dbPool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

async function main() {
  console.log('[Sitemap] Starting incremental generation run...');

  // Initialize engine with Cold/Hot Partitioning enabled
  const engine = new IncrementalSitemapEngine({
    outDir: '/var/www/static/sitemaps',
    baseUrl: 'https://example.com',
    locales: ['vi', 'en'],
    isIncremental: true,  // Automatically skips closed historical weeks
    isGzipEnabled: true,   // Emits .xml.gz alongside .xml for zero-CPU delivery
    itemsPerSitemap: 10_000,
    fetchRecords: async ({ sinceDate, limit, offset }) => {
      // Delta query: only fetch records modified since the target window
      const query = `
        SELECT slug, updated_at, priority, changefreq
        FROM articles
        WHERE updated_at >= $1 AND published = true
        ORDER BY updated_at DESC
        LIMIT $2 OFFSET $3
      `;
      const result = await dbPool.query(query, [sinceDate, limit, offset]);
      return result.rows.map((row) => ({
        loc: `https://example.com/posts/${row.slug}`,
        lastmod: row.updated_at.toISOString(),
        changefreq: row.changefreq || 'weekly',
        priority: row.priority || 0.8,
      }));
    },
  });

  const summary = await engine.run();
  console.log(`[Sitemap] Finished in ${summary.elapsedMs}ms:`);
  console.log(`  - Fresh files generated: ${summary.totalFilesGenerated}`);
  console.log(`  - Cold files skipped: ${summary.totalFilesSkipped}`);
}

main().catch(console.error);

2. High-Performance Nginx Zero-Copy Configuration (nginx.conf)

location /sitemaps/ {
    alias /var/www/static/sitemaps/;
    
    # 1. Zero-CPU pre-gzipped file delivery
    gzip_static on;
    
    # 2. Linux kernel zero-copy transfer directly from disk buffer
    sendfile on;
    tcp_nopush on;
    
    # 3. Aggressive caching headers for crawlers
    expires 1d;
    add_header Cache-Control "public, no-transform";
    
    # 4. Graceful fallback if a dynamic partition is pending
    try_files $uri $uri/ /api/sitemap-fallback;
}

⚡ Benchmark & Verified Performance

Benchmarked with k6 simulating 2,000 concurrent crawler requests on a 100,000 URL database:

Metric / ScenarioOn-Demand SSR (Next.js)Full Nightly Cron@kido/sitemap-incremental
Crawler Request TTFB480ms – 1,200ms18ms (static XML)0.8ms (Nginx gzip_static)
Generation Run TimeN/A (per-request)38.4 seconds0.42 seconds (Hot delta)
Database IOPS / Queries100,000 rows read/req100,000 rows nightly< 200 rows read (Hot only)
Node.js CPU under Crawl98% – 100% (High risk)0%0% (Origin completely idle)
504 Gateway Errors4.8% during crawler sweep0.0%0.00%

🚀 Surgical Extraction & CLI Installation

npx @kido/cli add sitemap-incremental

On this page