Skip to content
cccskills
FreePublished on GitHub

local-cache-idempotency-fallback

Use database cache when external APIs aren't reliably idempotent. Use when: (1) External API claims idempotency but returns different values for same input, (2) Re-running a script creates duplicate resources, (3) Need stable identifiers across runs but external service generates new ones. Pattern: check database cache first, only call external API for genuinely new items, cache the result.

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md4.1 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Local Cache as Idempotency Fallback

Problem

External APIs that should be idempotent (same input = same output) sometimes aren't. This causes problems when re-running scripts:

  • Duplicate resources created
  • Identifiers change between runs
  • State becomes inconsistent across systems

Context / Trigger Conditions

  • Re-running a script creates new resources instead of finding existing ones
  • External API "fixed" idempotency but still returns different values
  • Need stable identifiers (pubkeys, user IDs, resource IDs) across runs
  • Script works once but fails on subsequent runs due to changed IDs

Solution

Use database cache as the source of truth for idempotency:

// BEFORE: Always calls external API (brittle)
const { pubkey, token } = await externalApi.createUser(userId, username);
await db.saveUser({ userId, pubkey, token });

// AFTER: Check cache first (robust)
const cached = await db.getUser(userId);
let pubkey: string;
let token: string;

if (cached) {
  // Use cached values - stable across runs
  pubkey = cached.pubkey;
  token = cached.token;
  console.log(`Using cached pubkey: ${pubkey}`);
} else {
  // Only call external API for genuinely new items
  const result = await externalApi.createUser(userId, username);
  pubkey = result.pubkey;
  token = result.token;

  // Cache immediately for next run
  await db.saveUser({ userId, pubkey, token });
  console.log(`Created new pubkey: ${pubkey}`);
}

Key Pattern

  1. Check local first: Always query your database before calling external API
  2. Use cached values: If found, use local values even if stale
  3. Only create when missing: External API called only for genuinely new items
  4. Cache immediately: Save result right after successful API call
  5. Log the source: Indicate whether value is "(cached)" or "(new)" for debugging

Verification

  • Re-run script multiple times
  • Same identifier used each time (from cache)
  • No duplicate resources created in external system
  • Script is idempotent regardless of external API behavior

Example

Real-world application - Keycast account creation:

// Check if we have a cached account first (local DB is source of truth)
const cached = await db.getImportedUser(creator.user_id);
let pubkey: string;
let token: string;

if (cached) {
  // Use cached account - pubkey is stable
  pubkey = cached.pubkey;
  token = cached.token;
  console.log(`Pubkey: ${pubkey} (cached)`);
} else {
  // Create new account via external API
  const result = await keycast.createPreloadedUser(
    creator.user_id,
    username,
    displayName
  );
  pubkey = result.pubkey;
  token = result.token;
  console.log(`Pubkey: ${pubkey} (new)`);

  // Cache account in database immediately
  await db.saveImportedUser({
    vine_user_id: creator.user_id,
    username: creator.username,
    pubkey,
    token,
  });
}

Notes

  • This pattern works even when the external API claims to be idempotent
  • Database schema should use the input identifier as primary key (prevents duplicates)
  • Consider adding timestamps to track when cached values were created
  • For critical systems, add reconciliation logic to detect/fix drift
  • The cache becomes your source of truth - treat it accordingly

Related Patterns

  • Upsert on conflict: Use ON CONFLICT DO UPDATE to handle race conditions
  • Soft delete: Keep old records to track history of changes
  • Cache invalidation: Add TTL or manual refresh if external values can legitimately change

Related Skills

  • stale-cache-external-service-recovery: What to do when external service loses data and cached identifiers no longer exist (detect 404, delete cache, recreate)

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y". Use when: (1) ExternalSecret shows OutOfSync in ArgoCD but won't sync, (2) ArgoCD application status shows "namespace X is not permitted in project 'infrastructure'", (3) ExternalSecret targets a namespace managed by a different ArgoCD project, (4) Using apps-of-apps pattern with separate infrastructure and application projects.

The English summary is being prepared. Showing the original description.

divinevideo/divine-mobile266Updated Oct 19, 2026

Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language. After selection, generates AI image prompts and visual briefs section-by-section. Use when the user shares content and needs visual direction, image sourcing, or creative direction for any material.

The English summary is being prepared. Showing the original description.

divinevideo/divine-mobile266Updated Oct 19, 2026

Fix "Null check operator used on a null value" errors when an object is set to null during an async await. Use when: (1) Object reference is nullified while awaiting, (2) Code accesses object with ! after await returns, (3) Cancel/dispose operations run concurrently with async operations on same object. Solution: capture local reference before await.

The English summary is being prepared. Showing the original description.

divinevideo/divine-mobile266Updated Oct 19, 2026

Add custom metadata headers (x-amz-meta-*) to AWS v4 signed requests for GCS S3-compatible API. Use when: (1) Adding custom metadata to GCS uploads via S3 API, (2) Getting signature mismatch errors after adding new headers, (3) x-amz-meta-* headers being ignored or causing 403 errors. Custom headers MUST be included in canonical headers and signed headers list.

The English summary is being prepared. Showing the original description.

divinevideo/divine-mobile266Updated Oct 19, 2026

Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings. Use when: (1) Password authentication fails with correct password, (2) Secret created with `<<< "value"` syntax, (3) Error like "password authentication failed" or "invalid token" despite correct value. Bash here-strings (`<<<`) add a trailing newline that corrupts secrets.

The English summary is being prepared. Showing the original description.

divinevideo/divine-mobile266Updated Oct 19, 2026

Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp). Use when: (1) Media moderation, transcoding, or analysis silently skips files from Blossom or content-addressed storage servers, (2) URL extraction from Nostr event tags (imeta, r tags) drops URLs without recognized extensions, (3) CDN fallback URLs append .mp4 but the actual server uses extensionless content-addressed paths like /{sha256}. Common in Nostr video events (kind 34236) where different clients use different URL formats.

The English summary is being prepared. Showing the original description.

divinevideo/divine-mobile266Updated Oct 19, 2026

See all skills by divinevideo

Report a problem with this skill