{"id":"80cd8fef-de2a-445e-856d-7ef9fb291a88","shortId":"cUTmkf","kind":"skill","title":"database-patterns","tagline":"PostgreSQL + Redis database design patterns. Use for data modeling, indexing, caching strategies. Covers JSONB, tiered storage, cache consistency.","description":"# Database Patterns\n\n## Core Principles\n\n- **PostgreSQL Primary** — Relational data, transactions, complex queries\n- **Redis Secondary** — Caching, sessions, real-time data\n- **Index-First Design** — Design queries before indexes\n- **JSONB Sparingly** — Structured data prefers columns\n- **Cache-Aside Default** — Read-through, write-around\n- **Tiered Storage** — Hot/Warm/Cold data separation\n- **No backwards compatibility** — Migrate data, don't keep legacy schemas\n\n---\n\n## PostgreSQL\n\n### Data Type Selection\n\n| Use Case | Type | Avoid |\n|----------|------|-------|\n| Primary Key | `UUID` / `BIGSERIAL` | `INT` (range limits) |\n| Timestamps | `TIMESTAMPTZ` | `TIMESTAMP` (no timezone) |\n| Money | `NUMERIC(19,4)` | `FLOAT` (precision loss) |\n| Status | `TEXT` + CHECK | `INT` (unreadable) |\n| Semi-structured | `JSONB` | `JSON` (no indexing) |\n| Full-text | `TSVECTOR` | `LIKE '%..%'` |\n\n### Schema Design\n\n```sql\n-- Use UUID for distributed-friendly IDs\nCREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";\n\nCREATE TABLE users (\n  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),\n  email TEXT UNIQUE NOT NULL,\n  name TEXT NOT NULL,\n  status TEXT NOT NULL DEFAULT 'active'\n    CHECK (status IN ('active', 'inactive', 'suspended')),\n  metadata JSONB DEFAULT '{}',\n  created_at TIMESTAMPTZ DEFAULT NOW(),\n  updated_at TIMESTAMPTZ DEFAULT NOW()\n);\n\n-- Updated timestamp trigger\nCREATE OR REPLACE FUNCTION update_updated_at()\nRETURNS TRIGGER AS $$\nBEGIN\n  NEW.updated_at = NOW();\n  RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\nCREATE TRIGGER users_updated_at\n  BEFORE UPDATE ON users\n  FOR EACH ROW\n  EXECUTE FUNCTION update_updated_at();\n```\n\n### Indexing Strategy\n\n```sql\n-- B-Tree: Equality, range, sorting (default)\nCREATE INDEX idx_users_email ON users(email);\n\n-- Composite: Leftmost prefix rule\n-- Supports: (user_id), (user_id, created_at)\n-- Does NOT support: (created_at) alone\nCREATE INDEX idx_orders_user_date ON orders(user_id, created_at DESC);\n\n-- Partial: Reduce index size\nCREATE INDEX idx_active_users ON users(email)\n  WHERE status = 'active';\n\n-- GIN for JSONB: Containment queries\nCREATE INDEX idx_metadata ON users USING GIN (metadata jsonb_path_ops);\n\n-- Expression: Specific JSONB field\nCREATE INDEX idx_user_role ON users ((metadata->>'role'));\n\n-- Full-text search\nCREATE INDEX idx_search ON products USING GIN (to_tsvector('english', name || ' ' || description));\n```\n\n### JSONB Usage\n\n```sql\n-- Good: Dynamic attributes, rarely queried fields\nCREATE TABLE products (\n  id UUID PRIMARY KEY,\n  name TEXT NOT NULL,\n  price NUMERIC(19,4) NOT NULL,\n  category TEXT NOT NULL,           -- Extracted: frequently queried\n  attributes JSONB DEFAULT '{}'     -- Dynamic: color, size, specs\n);\n\n-- Query with containment\nSELECT * FROM products\nWHERE category = 'electronics'              -- B-Tree index\n  AND attributes @> '{\"brand\": \"Apple\"}';   -- GIN index\n\n-- Query specific field\nSELECT * FROM products\nWHERE attributes->>'color' = 'black';       -- Expression index\n\n-- Update JSONB field\nUPDATE products\nSET attributes = attributes || '{\"featured\": true}'\nWHERE id = '...';\n```\n\n### Query Optimization\n\n```sql\n-- Always use EXPLAIN ANALYZE\nEXPLAIN ANALYZE\nSELECT u.*, COUNT(o.id) as order_count\nFROM users u\nLEFT JOIN orders o ON o.user_id = u.id\nWHERE u.status = 'active'\nGROUP BY u.id\nORDER BY u.created_at DESC\nLIMIT 20;\n\n-- Watch for:\n-- ❌ Seq Scan on large tables → Add index\n-- ❌ Sort → Use index for ordering\n-- ❌ Nested Loop with many rows → Consider JOIN order\n-- ❌ Hash Join on huge tables → Add indexes\n```\n\n### Connection Pooling\n\n```typescript\n// PgBouncer or built-in pool\nimport { Pool } from 'pg';\n\nconst pool = new Pool({\n  max: 20,                      // Max connections\n  idleTimeoutMillis: 30000,     // Close idle connections\n  connectionTimeoutMillis: 2000, // Fail fast\n});\n\n// Connection count formula:\n// connections = (cores * 2) + effective_spindle_count\n// Usually 10-30 is enough\n```\n\n---\n\n## Redis\n\n### Data Structure Selection\n\n| Use Case | Structure | Example |\n|----------|-----------|---------|\n| Cache objects | String | `user:123` → JSON |\n| Counters | String + INCR | `views:article:456` |\n| Sessions | Hash | `session:abc` → {userId, ...} |\n| Leaderboards | Sorted Set | `scores` → {userId: score} |\n| Queues | List/Stream | `tasks` → LPUSH/RPOP |\n| Unique sets | Set | `online_users` |\n| Real-time | Pub/Sub/Stream | Notifications |\n\n### Key Naming\n\n```\n# Format: <entity>:<id>:<attribute>\nuser:123:profile\nuser:123:settings\norder:456:items\nsession:abc123\n\n# Use colons for hierarchy\n# Enables pattern matching with SCAN\nSCAN 0 MATCH \"user:*:profile\" COUNT 100\n```\n\n### TTL Strategy\n\n```typescript\nconst TTL = {\n  SESSION: 24 * 60 * 60,      // 24 hours\n  CACHE: 15 * 60,             // 15 minutes\n  RATE_LIMIT: 60,             // 1 minute\n  LOCK: 30,                   // 30 seconds\n};\n\n// Set with TTL\nawait redis.set(`cache:user:${id}`, JSON.stringify(user), 'EX', TTL.CACHE);\n\n// Check TTL\nconst remaining = await redis.ttl(`cache:user:${id}`);\n```\n\n---\n\n## Caching Patterns\n\n### Cache-Aside (Lazy Loading)\n\n```typescript\nasync function getUser(id: string): Promise<User> {\n  const cacheKey = `user:${id}`;\n\n  // 1. Check cache\n  const cached = await redis.get(cacheKey);\n  if (cached) {\n    return JSON.parse(cached);\n  }\n\n  // 2. Cache miss → Query database\n  const user = await db.user.findUnique({ where: { id } });\n  if (!user) {\n    throw new NotFoundError('User not found');\n  }\n\n  // 3. Populate cache\n  await redis.set(cacheKey, JSON.stringify(user), 'EX', 900);\n\n  return user;\n}\n```\n\n### Write-Through\n\n```typescript\nasync function updateUser(id: string, data: UpdateInput): Promise<User> {\n  // 1. Update database\n  const user = await db.user.update({\n    where: { id },\n    data,\n  });\n\n  // 2. Update cache immediately\n  await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 900);\n\n  return user;\n}\n```\n\n### Cache Invalidation\n\n```typescript\nasync function deleteUser(id: string): Promise<void> {\n  // 1. Delete from database\n  await db.user.delete({ where: { id } });\n\n  // 2. Invalidate cache\n  await redis.del(`user:${id}`);\n\n  // 3. Invalidate related caches\n  const keys = await redis.keys(`user:${id}:*`);\n  if (keys.length > 0) {\n    await redis.del(...keys);\n  }\n}\n```\n\n### Cache Stampede Prevention\n\n```typescript\nasync function getUserWithLock(id: string): Promise<User> {\n  const cacheKey = `user:${id}`;\n  const lockKey = `lock:user:${id}`;\n\n  // Check cache\n  const cached = await redis.get(cacheKey);\n  if (cached) {\n    return JSON.parse(cached);\n  }\n\n  // Try to acquire lock\n  const acquired = await redis.set(lockKey, '1', 'EX', 10, 'NX');\n\n  if (!acquired) {\n    // Another process is loading, wait and retry\n    await sleep(100);\n    return getUserWithLock(id);\n  }\n\n  try {\n    // Double-check cache (another process might have populated it)\n    const rechecked = await redis.get(cacheKey);\n    if (rechecked) {\n      return JSON.parse(rechecked);\n    }\n\n    // Load from database\n    const user = await db.user.findUnique({ where: { id } });\n    await redis.set(cacheKey, JSON.stringify(user), 'EX', 900);\n    return user;\n  } finally {\n    await redis.del(lockKey);\n  }\n}\n```\n\n### Cache Penetration Prevention\n\n```typescript\nasync function getUserSafe(id: string): Promise<User | null> {\n  const cacheKey = `user:${id}`;\n\n  const cached = await redis.get(cacheKey);\n\n  // Check for cached null\n  if (cached === 'NULL') {\n    return null;\n  }\n\n  if (cached) {\n    return JSON.parse(cached);\n  }\n\n  const user = await db.user.findUnique({ where: { id } });\n\n  if (!user) {\n    // Cache null with short TTL\n    await redis.set(cacheKey, 'NULL', 'EX', 60);\n    return null;\n  }\n\n  await redis.set(cacheKey, JSON.stringify(user), 'EX', 900);\n  return user;\n}\n```\n\n---\n\n## Tiered Storage\n\n```\n┌─────────────────────────────────────────────────┐\n│                   Application                    │\n└─────────────────────────────────────────────────┘\n                        │\n        ┌───────────────┼───────────────┐\n        ▼               ▼               ▼\n   ┌─────────┐    ┌─────────┐    ┌─────────┐\n   │  Redis  │    │ Postgres │    │ Archive │\n   │  (Hot)  │    │  (Warm)  │    │  (Cold) │\n   └─────────┘    └─────────┘    └─────────┘\n\n   < 1ms          ~10ms           ~100ms+\n   Active data    Recent data     Historical\n   Memory         SSD             Object storage\n```\n\n### Partitioning for Cold Data\n\n```sql\n-- Partition by date range\nCREATE TABLE orders (\n  id UUID NOT NULL,\n  user_id UUID NOT NULL,\n  total NUMERIC(19,4) NOT NULL,\n  created_at TIMESTAMPTZ NOT NULL\n) PARTITION BY RANGE (created_at);\n\n-- Create partitions\nCREATE TABLE orders_2025_q1 PARTITION OF orders\n  FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');\n\nCREATE TABLE orders_2025_q2 PARTITION OF orders\n  FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');\n\n-- Archive old data\nCREATE TABLE orders_archive (LIKE orders INCLUDING ALL);\n\n-- Move old data to archive\nWITH moved AS (\n  DELETE FROM orders\n  WHERE created_at < NOW() - INTERVAL '1 year'\n  RETURNING *\n)\nINSERT INTO orders_archive SELECT * FROM moved;\n```\n\n---\n\n## Transactions\n\n### ACID Compliance\n\n```typescript\n// Use transactions for multi-table operations\nasync function transferFunds(fromId: string, toId: string, amount: number) {\n  await db.$transaction(async (tx) => {\n    // Deduct from source\n    const from = await tx.account.update({\n      where: { id: fromId },\n      data: { balance: { decrement: amount } },\n    });\n\n    if (from.balance < 0) {\n      throw new Error('Insufficient funds');\n    }\n\n    // Add to destination\n    await tx.account.update({\n      where: { id: toId },\n      data: { balance: { increment: amount } },\n    });\n  });\n}\n```\n\n### Optimistic Locking\n\n```sql\n-- Add version column\nALTER TABLE products ADD COLUMN version INT DEFAULT 1;\n\n-- Update with version check\nUPDATE products\nSET\n  stock = stock - 1,\n  version = version + 1\nWHERE id = $1 AND version = $2\nRETURNING *;\n\n-- If no rows returned, concurrent modification occurred\n```\n\n---\n\n## Checklist\n\n```markdown\n## Schema\n- [ ] UUID or BIGSERIAL for primary keys\n- [ ] TIMESTAMPTZ for all timestamps\n- [ ] NUMERIC for money, not FLOAT\n- [ ] CHECK constraints for enums\n- [ ] Foreign keys with ON DELETE\n\n## Indexing\n- [ ] Index for each WHERE clause pattern\n- [ ] Composite indexes match query order\n- [ ] GIN index for JSONB containment\n- [ ] EXPLAIN ANALYZE for slow queries\n\n## Caching\n- [ ] Cache-aside as default pattern\n- [ ] TTL on all cached data\n- [ ] Cache invalidation on writes\n- [ ] Stampede/penetration protection\n\n## Operations\n- [ ] Connection pooling configured\n- [ ] Slow query logging enabled\n- [ ] Backup and recovery tested\n- [ ] Partition strategy for growth\n```\n\n---\n\n## See Also\n\n- [reference/postgresql.md](reference/postgresql.md) — PostgreSQL deep dive\n- [reference/redis.md](reference/redis.md) — Redis patterns\n- [reference/caching.md](reference/caching.md) — Caching strategies","tags":["database","patterns","claude","arsenal","majiayu000","agent-skills","ai-agents","ai-coding-assistant","automation","claude-code","code-review","developer-tools"],"capabilities":["skill","source-majiayu000","skill-database-patterns","topic-agent-skills","topic-ai-agents","topic-ai-coding-assistant","topic-automation","topic-claude","topic-claude-code","topic-code-review","topic-developer-tools","topic-devops","topic-productivity","topic-prompt-engineering","topic-python"],"categories":["claude-arsenal"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/majiayu000/claude-arsenal/database-patterns","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add majiayu000/claude-arsenal","source_repo":"https://github.com/majiayu000/claude-arsenal","install_from":"skills.sh"}},"qualityScore":"0.464","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 29 github stars · SKILL.md body (10,934 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-01T07:01:13.406Z","embedding":null,"createdAt":"2026-04-18T22:24:07.180Z","updatedAt":"2026-05-01T07:01:13.406Z","lastSeenAt":"2026-05-01T07:01:13.406Z","tsv":"'-01':1030,1031,1035,1049,1053 '-04':1034,1048 '-07':1052 '-30':529 '0':601,787,1132 '1':626,671,727,760,831,1081,1164,1174,1177,1180 '10':528,833 '100':606,846 '100ms':969 '10ms':968 '123':544,581,584 '15':619,621 '19':102,358,1002 '1ms':967 '2':523,684,737,768,1183 '20':458,506 '2000':515 '2025':1021,1029,1033,1039,1047,1051 '24':613,616 '3':703,775 '30':629,630 '30000':510 '4':103,359,1003 '456':551,587 '60':614,615,620,625,946 '900':712,748,886,955 'abc':555 'abc123':590 'acid':1092 'acquir':824,827,836 'activ':167,171,281,288,448,970 'add':466,486,1138,1153,1159 'alon':260 'also':1276 'alter':1156 'alway':422 'amount':1109,1129,1149 'analyz':425,427,1237 'anoth':837,855 'appl':392 'applic':960 'archiv':963,1054,1060,1069,1087 'around':64 'articl':550 'asid':57,657,1244 'async':661,719,754,795,897,1102,1114 'attribut':341,369,390,402,413,414 'avoid':87 'await':635,648,676,691,706,732,741,764,771,781,788,814,828,844,863,876,880,890,911,930,941,949,1111,1121,1141 'b':230,386 'b-tree':229,385 'backup':1267 'backward':71 'balanc':1127,1147 'begin':200 'bigseri':91,1197 'black':404 'brand':391 'built':494 'built-in':493 'cach':14,20,35,56,540,618,637,650,653,656,673,675,680,683,685,705,739,751,770,778,791,811,813,818,821,854,893,910,916,919,924,927,936,1241,1243,1251,1253,1288 'cache-asid':55,655,1242 'cachekey':668,678,708,802,816,865,882,906,913,943,951 'case':85,537 'categori':362,383 'check':109,168,644,672,810,853,914,1168,1210 'checklist':1192 'claus':1224 'close':511 'cold':966,981 'colon':592 'color':373,403 'column':54,1155,1160 'compat':72 'complex':31 'complianc':1093 'composit':244,1226 'concurr':1189 'configur':1262 'connect':488,508,513,518,521,1260 'connectiontimeoutmilli':514 'consid':478 'consist':21 'const':501,610,646,667,674,689,730,779,801,805,812,826,861,874,905,909,928,1119 'constraint':1211 'contain':292,378,1235 'core':24,522 'count':430,434,519,526,605 'counter':546 'cover':16 'creat':134,142,177,190,209,236,253,258,261,271,278,294,310,323,345,988,1006,1014,1016,1018,1036,1057,1077 'data':11,29,40,52,68,74,81,533,724,736,971,973,982,1056,1067,1126,1146,1252 'databas':2,6,22,688,729,763,873 'database-pattern':1 'date':266,986 'db':1112 'db.user.delete':765 'db.user.findunique':692,877,931 'db.user.update':733 'decrement':1128 'deduct':1116 'deep':1280 'default':58,149,166,176,180,185,235,371,1163,1246 'delet':761,1073,1218 'deleteus':756 'desc':273,456 'descript':335 'design':7,44,45,125 'destin':1140 'distribut':131 'distributed-friend':130 'dive':1281 'doubl':852 'double-check':851 'dynam':340,372 'effect':524 'electron':384 'email':153,240,243,285 'enabl':595,1266 'end':206 'english':333 'enough':531 'enum':1213 'equal':232 'error':1135 'ex':642,711,747,832,885,945,954 'exampl':539 'execut':221 'exist':138 'explain':424,426,1236 'express':306,405 'extens':135 'extract':366 'fail':516 'fast':517 'featur':415 'field':309,344,397,409 'final':889 'first':43 'float':104,1209 'foreign':1214 'format':579 'formula':520 'found':702 'frequent':367 'friend':132 'from.balance':1131 'fromid':1105,1125 'full':120,320 'full-text':119,319 'function':193,222,662,720,755,796,898,1103 'fund':1137 'generat':151 'getus':663 'getusersaf':899 'getuserwithlock':797,848 'gin':289,301,330,393,1231 'good':339 'group':449 'growth':1274 'hash':481,553 'hierarchi':594 'histor':974 'hot':964 'hot/warm/cold':67 'hour':617 'huge':484 'id':133,145,250,252,270,348,418,444,639,652,664,670,694,722,735,744,757,767,774,784,798,804,809,849,879,900,908,933,991,996,1124,1144,1179 'idl':512 'idletimeoutmilli':509 'idx':238,263,280,296,312,325 'immedi':740 'import':497 'inact':172 'includ':1063 'incr':548 'increment':1148 'index':13,42,48,118,226,237,262,276,279,295,311,324,388,394,406,467,470,487,1219,1220,1227,1232 'index-first':41 'insert':1084 'insuffici':1136 'int':92,110,1162 'interv':1080 'invalid':752,769,776,1254 'item':588 'join':439,479,482 'json':116,545 'json.parse':682,820,869,926 'json.stringify':640,709,745,883,952 'jsonb':17,49,115,175,291,303,308,336,370,408,1234 'keep':77 'key':89,148,351,577,780,790,1200,1215 'keys.length':786 'languag':207 'larg':464 'lazi':658 'leaderboard':557 'left':438 'leftmost':245 'legaci':78 'like':123,1061 'limit':94,457,624 'list/stream':564 'load':659,840,871 'lock':628,807,825,1151 'lockkey':806,830,892 'log':1265 'loop':474 'loss':106 'lpush/rpop':566 'mani':476 'markdown':1193 'match':597,602,1228 'max':505,507 'memori':975 'metadata':174,297,302,317 'might':857 'migrat':73 'minut':622,627 'miss':686 'model':12 'modif':1190 'money':100,1207 'move':1065,1071,1090 'multi':1099 'multi-t':1098 'name':158,334,352,578 'nest':473 'new':205,503,698,1134 'new.updated':201 'notfounderror':699 'notif':576 'null':157,161,165,355,361,365,904,917,920,922,937,944,948,994,999,1005,1010 'number':1110 'numer':101,357,1001,1205 'nx':834 'o':441 'o.id':431 'o.user':443 'object':541,977 'occur':1191 'old':1055,1066 'onlin':570 'op':305 'oper':1101,1259 'optim':420 'optimist':1150 'order':264,268,433,440,452,472,480,586,990,1020,1025,1038,1043,1059,1062,1075,1086,1230 'ossp':141 'partial':274 'partit':979,984,1011,1017,1023,1041,1271 'path':304 'pattern':3,8,23,596,654,1225,1247,1285 'penetr':894 'pg':500 'pgbouncer':491 'plpgsql':208 'pool':489,496,498,502,504,1261 'popul':704,859 'postgr':962 'postgresql':4,26,80,1279 'precis':105 'prefer':53 'prefix':246 'prevent':793,895 'price':356 'primari':27,88,147,350,1199 'principl':25 'process':838,856 'product':328,347,381,400,411,1158,1170 'profil':582,604 'promis':666,726,759,800,902 'protect':1258 'pub/sub/stream':575 'q1':1022 'q2':1040 'queri':32,46,293,343,368,376,395,419,687,1229,1240,1264 'queue':563 'rang':93,233,987,1013 'rare':342 'rate':623 'read':60 'read-through':59 'real':38,573 'real-tim':37,572 'recent':972 'recheck':862,867,870 'recoveri':1269 'redi':5,33,532,961,1284 'redis.del':772,789,891 'redis.get':677,815,864,912 'redis.keys':782 'redis.set':636,707,742,829,881,942,950 'redis.ttl':649 'reduc':275 'reference/caching.md':1286,1287 'reference/postgresql.md':1277,1278 'reference/redis.md':1282,1283 'relat':28,777 'remain':647 'replac':192 'retri':843 'return':197,204,681,713,749,819,847,868,887,921,925,947,956,1083,1184,1188 'role':314,318 'row':220,477,1187 'rule':247 'scan':462,599,600 'schema':79,124,1194 'score':560,562 'search':322,326 'second':631 'secondari':34 'see':1275 'select':83,379,398,428,535,1088 'semi':113 'semi-structur':112 'separ':69 'seq':461 'session':36,552,554,589,612 'set':412,559,568,569,585,632,1171 'short':939 'size':277,374 'skill' 'skill-database-patterns' 'sleep':845 'slow':1239,1263 'sort':234,468,558 'sourc':1118 'source-majiayu000' 'spare':50 'spec':375 'specif':307,396 'spindl':525 'sql':126,228,338,421,983,1152 'ssd':976 'stamped':792 'stampede/penetration':1257 'status':107,162,169,287 'stock':1172,1173 'storag':19,66,959,978 'strategi':15,227,608,1272,1289 'string':542,547,665,723,758,799,901,1106,1108 'structur':51,114,534,538 'support':248,257 'suspend':173 'tabl':143,346,465,485,989,1019,1037,1058,1100,1157 'task':565 'test':1270 'text':108,121,154,159,163,321,353,363 'throw':697,1133 'tier':18,65,958 'time':39,574 'timestamp':95,97,188,1204 'timestamptz':96,179,184,1008,1201 'timezon':99 'toid':1107,1145 'topic-agent-skills' 'topic-ai-agents' 'topic-ai-coding-assistant' 'topic-automation' 'topic-claude' 'topic-claude-code' 'topic-code-review' 'topic-developer-tools' 'topic-devops' 'topic-productivity' 'topic-prompt-engineering' 'topic-python' 'total':1000 'transact':30,1091,1096,1113 'transferfund':1104 'tree':231,387 'tri':822,850 'trigger':189,198,210 'true':416 'tsvector':122,332 'ttl':607,611,634,645,940,1248 'ttl.cache':643 'tx':1115 'tx.account.update':1122,1142 'type':82,86 'typescript':490,609,660,718,753,794,896,1094 'u':429,437 'u.created':454 'u.id':445,451 'u.status':447 'uniqu':155,567 'unread':111 'updat':182,187,194,195,212,215,223,224,407,410,728,738,1165,1169 'updateinput':725 'updateus':721 'usag':337 'use':9,84,127,300,329,423,469,536,591,1095 'user':144,211,217,239,242,249,251,265,269,282,284,299,313,316,436,543,571,580,583,603,638,641,651,669,690,696,700,710,714,731,743,746,750,773,783,803,808,875,884,888,903,907,929,935,953,957,995 'userid':556,561 'usual':527 'uuid':90,128,140,146,150,349,992,997,1195 'uuid-ossp':139 'v4':152 'valu':1027,1045 'version':1154,1161,1167,1175,1176,1182 'view':549 'wait':841 'warm':965 'watch':459 'write':63,716,1256 'write-around':62 'write-through':715 'year':1082","prices":[{"id":"c7d73d80-a678-4e15-ae68-9730ee6a0063","listingId":"80cd8fef-de2a-445e-856d-7ef9fb291a88","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"majiayu000","category":"claude-arsenal","install_from":"skills.sh"},"createdAt":"2026-04-18T22:24:07.180Z"}],"sources":[{"listingId":"80cd8fef-de2a-445e-856d-7ef9fb291a88","source":"github","sourceId":"majiayu000/claude-arsenal/database-patterns","sourceUrl":"https://github.com/majiayu000/claude-arsenal/tree/main/skills/database-patterns","isPrimary":false,"firstSeenAt":"2026-04-18T22:24:07.180Z","lastSeenAt":"2026-05-01T07:01:13.406Z"}],"details":{"listingId":"80cd8fef-de2a-445e-856d-7ef9fb291a88","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"majiayu000","slug":"database-patterns","github":{"repo":"majiayu000/claude-arsenal","stars":29,"topics":["agent-skills","ai-agents","ai-coding-assistant","automation","claude","claude-code","code-review","developer-tools","devops","productivity","prompt-engineering","python","software-development","typescript","workflows"],"license":"mit","html_url":"https://github.com/majiayu000/claude-arsenal","pushed_at":"2026-04-29T04:12:22Z","description":"52 production-ready Claude Code skills and 7 specialized agents for software development, DevOps, product workflows, and automation.","skill_md_sha":"a3e33c7144ae99e63eaca9d3dbd7098b91962fc8","skill_md_path":"skills/database-patterns/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/majiayu000/claude-arsenal/tree/main/skills/database-patterns"},"layout":"multi","source":"github","category":"claude-arsenal","frontmatter":{"name":"database-patterns","description":"PostgreSQL + Redis database design patterns. Use for data modeling, indexing, caching strategies. Covers JSONB, tiered storage, cache consistency."},"skills_sh_url":"https://skills.sh/majiayu000/claude-arsenal/database-patterns"},"updatedAt":"2026-05-01T07:01:13.406Z"}}