everything-gemini 1.2.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
- package/.github/ISSUE_TEMPLATE/subagent_proposal.md +29 -0
- package/.github/workflows/ci.yml +72 -1
- package/.github/workflows/release.yml +17 -2
- package/CHANGELOG.md +178 -0
- package/CONTRIBUTING.md +90 -4
- package/README.md +147 -23
- package/action.yml +35 -0
- package/agents/browser-qa.md +1 -1
- package/agents/database-migrator.md +26 -0
- package/agents/documentation-writer.md +27 -0
- package/agents/incident-responder.md +34 -0
- package/agents/performance-tuner.md +27 -0
- package/agents/platform-engineer.md +33 -0
- package/agents/release-engineer.md +34 -0
- package/bin/everything-gemini +1791 -142
- package/docs/COMMUNITY_SUBMISSIONS.md +88 -0
- package/docs/LAUNCH_KIT.md +138 -13
- package/docs/architecture/progressive-disclosure.md +3 -3
- package/docs/index.html +410 -40
- package/docs/launch-kit.md +163 -0
- package/examples/cli-workflows/demo_workflow.sh +15 -7
- package/examples/python-sdk/README.md +32 -5
- package/examples/python-sdk/documentation_audit_pipeline.py +83 -0
- package/examples/python-sdk/incident_triage_pipeline.py +170 -0
- package/examples/python-sdk/multi_agent_pipeline.py +95 -0
- package/examples/python-sdk/platform_provisioning_pipeline.py +193 -0
- package/examples/python-sdk/release_readiness_audit.py +144 -0
- package/hooks/secret-scrubber/hooks.json +16 -0
- package/hooks/secret-scrubber/scripts/scrub-secrets.sh +62 -0
- package/mcp/README.md +21 -0
- package/mcp/mcp_config.example.json +39 -0
- package/package.json +3 -1
- package/plugins/core/rules/core-practices.md +63 -4
- package/plugins/python-ai/rules/ai-agent-patterns.md +67 -4
- package/plugins/security-shield/rules/zero-trust.md +60 -4
- package/plugins/web-fullstack/rules/frontend-standards.md +74 -4
- package/rules/paradigms/security-hardened.md +53 -10
- package/rules/paradigms/tdd.md +65 -8
- package/scripts/install.ps1 +14 -0
- package/scripts/install.sh +15 -1
- package/scripts/validate.sh +103 -12
- package/skills/apache-kafka-streams/SKILL.md +152 -0
- package/skills/argo-workflows/SKILL.md +151 -0
- package/skills/axum-web-service/SKILL.md +159 -0
- package/skills/cilium-ebpf-networking/SKILL.md +145 -0
- package/skills/clickhouse-analytics/SKILL.md +128 -0
- package/skills/cloudflare-r2-object-storage/SKILL.md +143 -0
- package/skills/dbt-transformation/SKILL.md +179 -0
- package/skills/debezium-cdc/SKILL.md +166 -0
- package/skills/distributed-rate-limiting/SKILL.md +138 -0
- package/skills/duckdb-local-olap/SKILL.md +134 -0
- package/skills/fastapi-production/SKILL.md +157 -0
- package/skills/grpc-connect-rpc/SKILL.md +176 -0
- package/skills/langgraph-orchestrator/SKILL.md +144 -0
- package/skills/litellm-proxy-gateway/SKILL.md +154 -0
- package/skills/milvus-vector-clustering/SKILL.md +134 -0
- package/skills/nats-jetstream/SKILL.md +122 -0
- package/skills/open-telemetry-tracing/SKILL.md +151 -0
- package/skills/playwright-visual-regression/SKILL.md +124 -0
- package/skills/redis-cache-patterns/SKILL.md +159 -0
- package/skills/svelte-kit-architect/SKILL.md +190 -0
- package/skills/temporal-workflows/SKILL.md +158 -0
- package/skills/trpc-react-query/SKILL.md +169 -0
- package/skills/turborepo-monorepo/SKILL.md +199 -0
- package/skills/vault-secrets-management/SKILL.md +129 -0
- package/skills/vllm-inference/SKILL.md +144 -0
- package/skills/wasm-rust-optimizer/SKILL.md +130 -0
- package/templates/full-power/hooks.json +14 -0
- package/templates/full-power/mcp_config.json +47 -0
- package/templates/recommended/hooks.json +14 -0
- package/tests/cli.test.js +447 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloudflare-r2-object-storage
|
|
3
|
+
description: >-
|
|
4
|
+
Architects, configures, and integrates Cloudflare R2 zero-egress object storage using S3-compatible APIs, pre-signed upload URLs, multipart streaming, and edge CDN distribution.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cloudflare R2 Object Storage & Edge Distribution
|
|
8
|
+
|
|
9
|
+
This skill guides the implementation of zero-egress fee object storage using Cloudflare R2 with S3-compatible APIs, pre-signed URLs, multipart uploads, and edge CDN distribution.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ⚡ Architectural Comparison: R2 vs Standard S3
|
|
14
|
+
|
|
15
|
+
| Feature | Cloudflare R2 | Standard AWS S3 |
|
|
16
|
+
| :--- | :--- | :--- |
|
|
17
|
+
| **Data Egress Fees** | **$0.00 / GB (Free)** | ~$0.09 / GB |
|
|
18
|
+
| **API Compatibility** | Full S3 API v4 signature | Native AWS S3 |
|
|
19
|
+
| **Edge Integration** | Native Cloudflare Workers & CDN | CloudFront integration required |
|
|
20
|
+
| **Storage Pricing** | $0.015 / GB-month | $0.023 / GB-month |
|
|
21
|
+
| **Global Replication** | Automatic across edge locations | Cross-Region Replication (CRR) billed |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🛠️ TypeScript S3 Client Configuration (`@aws-sdk/client-s3`)
|
|
26
|
+
|
|
27
|
+
R2 endpoints use account-specific URLs with AWS Signature Version 4:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
|
|
31
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
32
|
+
|
|
33
|
+
const ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID!;
|
|
34
|
+
const R2_ACCESS_KEY_ID = process.env.R2_ACCESS_KEY_ID!;
|
|
35
|
+
const R2_SECRET_ACCESS_KEY = process.env.R2_SECRET_ACCESS_KEY!;
|
|
36
|
+
|
|
37
|
+
export const r2Client = new S3Client({
|
|
38
|
+
region: 'auto',
|
|
39
|
+
endpoint: `https://${ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
|
40
|
+
credentials: {
|
|
41
|
+
accessKeyId: R2_ACCESS_KEY_ID,
|
|
42
|
+
secretAccessKey: R2_SECRET_ACCESS_KEY,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 📤 Pre-Signed URLs for Direct Browser Uploads
|
|
50
|
+
|
|
51
|
+
Prevent API server bottlenecking by generating short-lived pre-signed upload URLs:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
export async function generateUploadUrl(key: string, contentType: string, expiresInSeconds = 300) {
|
|
55
|
+
const command = new PutObjectCommand({
|
|
56
|
+
Bucket: 'enterprise-artifacts',
|
|
57
|
+
Key: key,
|
|
58
|
+
ContentType: contentType,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const uploadUrl = await getSignedUrl(r2Client, command, { expiresIn: expiresInSeconds });
|
|
62
|
+
return {
|
|
63
|
+
uploadUrl,
|
|
64
|
+
key,
|
|
65
|
+
publicUrl: `https://cdn.example.com/${key}`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 📦 High-Throughput Multipart Uploads (Node.js / Python)
|
|
73
|
+
|
|
74
|
+
For files exceeding 100MB, use parallel multipart uploads with the Upload helper:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Upload } from '@aws-sdk/lib-storage';
|
|
78
|
+
import fs from 'node:fs';
|
|
79
|
+
|
|
80
|
+
export async function uploadLargeArchive(filePath: string, destinationKey: string) {
|
|
81
|
+
const fileStream = fs.createReadStream(filePath);
|
|
82
|
+
|
|
83
|
+
const parallelUpload = new Upload({
|
|
84
|
+
client: r2Client,
|
|
85
|
+
params: {
|
|
86
|
+
Bucket: 'enterprise-artifacts',
|
|
87
|
+
Key: destinationKey,
|
|
88
|
+
Body: fileStream,
|
|
89
|
+
ContentType: 'application/octet-stream',
|
|
90
|
+
},
|
|
91
|
+
queueSize: 4, // 4 concurrent part uploads
|
|
92
|
+
partSize: 10 * 1024 * 1024, // 10MB chunk size
|
|
93
|
+
leavePartsOnError: false,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
parallelUpload.on('httpUploadProgress', (progress) => {
|
|
97
|
+
console.log(`Uploaded ${progress.loaded} of ${progress.total} bytes`);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return await parallelUpload.done();
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 🌐 Custom Domain & Cloudflare CDN Caching Rule
|
|
107
|
+
|
|
108
|
+
Expose R2 buckets through a custom domain with edge cache headers:
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
// Cloudflare Worker Edge Proxy
|
|
112
|
+
export default {
|
|
113
|
+
async fetch(request, env) {
|
|
114
|
+
const url = new URL(request.url);
|
|
115
|
+
const key = url.pathname.slice(1);
|
|
116
|
+
|
|
117
|
+
if (request.method !== 'GET') {
|
|
118
|
+
return new Response('Method Not Allowed', { status: 405 });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const object = await env.MY_R2_BUCKET.get(key);
|
|
122
|
+
if (!object) {
|
|
123
|
+
return new Response('Not Found', { status: 404 });
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const headers = new Headers();
|
|
127
|
+
object.writeHttpMetadata(headers);
|
|
128
|
+
headers.set('etag', object.httpEtag);
|
|
129
|
+
headers.set('Cache-Control', 'public, max-age=31536000, immutable');
|
|
130
|
+
|
|
131
|
+
return new Response(object.body, { headers });
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 📋 Architectural Rules & Best Practices
|
|
139
|
+
|
|
140
|
+
1. **Bucket Region**: Always set region to `auto` in AWS S3 SDK clients; R2 automatically routes storage to the lowest-latency regional cluster.
|
|
141
|
+
2. **CORS Headers**: Set permissive CORS rules (`AllowedOrigins`, `AllowedMethods: ["PUT", "GET"]`) on the R2 bucket for direct browser pre-signed uploads.
|
|
142
|
+
3. **Multipart Cleanup**: Configure bucket lifecycle rules to automatically abort incomplete multipart uploads after 7 days (`AbortIncompleteMultipartUpload`).
|
|
143
|
+
4. **Zero-Egress Strategy**: Route public media and file downloads through Cloudflare Custom Domains or Workers to benefit from zero bandwidth charges.
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dbt-transformation
|
|
3
|
+
description: >-
|
|
4
|
+
Designs, develops, and tests modular SQL data transformations using dbt (data build tool), Jinja macros, semantic layer metrics, automated schema tests, and documentation generation.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# dbt Modular Transformation & Data Modeling
|
|
8
|
+
|
|
9
|
+
This skill guides structuring production-grade ELT pipelines using dbt (Core & Cloud), enforcing modular layer architecture (staging, intermediate, marts), DRY Jinja macros, automated data testing, and incremental table materialization.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ⚡ Core Patterns
|
|
14
|
+
|
|
15
|
+
### 1. Multi-Layer Dimensional Modeling Architecture
|
|
16
|
+
|
|
17
|
+
Organize transformations into clean, progressive DAG layers:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
models/
|
|
21
|
+
├── staging/ # 1:1 view on raw sources (renaming, casting)
|
|
22
|
+
│ ├── stripe/
|
|
23
|
+
│ │ ├── _stripe__sources.yml
|
|
24
|
+
│ │ ├── _stripe__models.yml
|
|
25
|
+
│ │ └── stg_stripe__payments.sql
|
|
26
|
+
├── intermediate/ # Business logic joins & window aggregations
|
|
27
|
+
│ └── finance/
|
|
28
|
+
│ ├── _int_finance__models.yml
|
|
29
|
+
│ └── int_payments_pivoted_to_orders.sql
|
|
30
|
+
└── marts/ # Dimensional star schema (facts & dimensions)
|
|
31
|
+
├── core/
|
|
32
|
+
│ ├── fct_orders.sql
|
|
33
|
+
│ └── dim_customers.sql
|
|
34
|
+
└── marketing/
|
|
35
|
+
└── fct_campaign_conversions.sql
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
### 2. Staging Layer Model with CTE Standards
|
|
41
|
+
|
|
42
|
+
Clean raw source tables into normalized relational types:
|
|
43
|
+
|
|
44
|
+
```sql
|
|
45
|
+
-- models/staging/stripe/stg_stripe__payments.sql
|
|
46
|
+
with source as (
|
|
47
|
+
select * from {{ source('stripe', 'payment') }}
|
|
48
|
+
),
|
|
49
|
+
|
|
50
|
+
renamed as (
|
|
51
|
+
select
|
|
52
|
+
id as payment_id,
|
|
53
|
+
orderid as order_id,
|
|
54
|
+
paymentmethod as payment_method,
|
|
55
|
+
status as payment_status,
|
|
56
|
+
-- Amount stored in cents; convert to dollars
|
|
57
|
+
round(amount / 100.0, 2) as amount_usd,
|
|
58
|
+
created as created_at
|
|
59
|
+
from source
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
select * from renamed
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Declare tests and column descriptions in accompanying YAML:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
# models/staging/stripe/_stripe__models.yml
|
|
69
|
+
version: 2
|
|
70
|
+
|
|
71
|
+
models:
|
|
72
|
+
- name: stg_stripe__payments
|
|
73
|
+
description: "Normalized payments table from Stripe webhook ingest."
|
|
74
|
+
columns:
|
|
75
|
+
- name: payment_id
|
|
76
|
+
description: "Primary key for payment events."
|
|
77
|
+
tests:
|
|
78
|
+
- unique
|
|
79
|
+
- not_null
|
|
80
|
+
- name: payment_status
|
|
81
|
+
tests:
|
|
82
|
+
- accepted_values:
|
|
83
|
+
values: ['success', 'fail', 'pending']
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### 3. High-Performance Incremental Fact Table
|
|
89
|
+
|
|
90
|
+
Process only newly ingested or updated records using `is_incremental()`:
|
|
91
|
+
|
|
92
|
+
```sql
|
|
93
|
+
-- models/marts/core/fct_orders.sql
|
|
94
|
+
{{
|
|
95
|
+
config(
|
|
96
|
+
materialized='incremental',
|
|
97
|
+
unique_key='order_id',
|
|
98
|
+
incremental_strategy='merge',
|
|
99
|
+
on_schema_change='fail'
|
|
100
|
+
)
|
|
101
|
+
}}
|
|
102
|
+
|
|
103
|
+
with orders as (
|
|
104
|
+
select * from {{ ref('stg_jaffle_shop__orders') }}
|
|
105
|
+
{% if is_incremental() %}
|
|
106
|
+
-- Only query rows created or modified in the last 3 days for backfill resilience
|
|
107
|
+
where order_date >= (select dateadd('day', -3, max(order_date)) from {{ this }})
|
|
108
|
+
{% endif %}
|
|
109
|
+
),
|
|
110
|
+
|
|
111
|
+
payments as (
|
|
112
|
+
select * from {{ ref('stg_stripe__payments') }}
|
|
113
|
+
),
|
|
114
|
+
|
|
115
|
+
order_payments as (
|
|
116
|
+
select
|
|
117
|
+
order_id,
|
|
118
|
+
sum(case when payment_status = 'success' then amount_usd else 0 end) as amount_usd
|
|
119
|
+
from payments
|
|
120
|
+
group by 1
|
|
121
|
+
),
|
|
122
|
+
|
|
123
|
+
final as (
|
|
124
|
+
select
|
|
125
|
+
orders.order_id,
|
|
126
|
+
orders.customer_id,
|
|
127
|
+
orders.order_date,
|
|
128
|
+
orders.status as order_status,
|
|
129
|
+
coalesce(order_payments.amount_usd, 0) as total_amount_usd
|
|
130
|
+
from orders
|
|
131
|
+
left join order_payments on orders.order_id = order_payments.order_id
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
select * from final
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### 4. Custom Jinja Macro for Dynamic Pivoting
|
|
140
|
+
|
|
141
|
+
Avoid repetitive boilerplate using parameterized Jinja macros:
|
|
142
|
+
|
|
143
|
+
```sql
|
|
144
|
+
-- macros/pivot_sum.sql
|
|
145
|
+
{% macro pivot_sum(column_name, values_list, aggregate_col='amount_usd') %}
|
|
146
|
+
{% for val in values_list %}
|
|
147
|
+
sum(case when {{ column_name }} = '{{ val }}' then {{ aggregate_col }} else 0 end) as {{ val }}_amount
|
|
148
|
+
{% if not loop.last %},{% endif %}
|
|
149
|
+
{% endfor %}
|
|
150
|
+
{% endmacro %}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Usage in intermediate model:
|
|
154
|
+
|
|
155
|
+
```sql
|
|
156
|
+
select
|
|
157
|
+
order_id,
|
|
158
|
+
{{ pivot_sum('payment_method', ['credit_card', 'coupon', 'bank_transfer', 'gift_card']) }}
|
|
159
|
+
from {{ ref('stg_stripe__payments') }}
|
|
160
|
+
group by 1
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 🎯 Verification & Diagnostics
|
|
166
|
+
|
|
167
|
+
1. **Compile SQL with Resolved Jinja**:
|
|
168
|
+
```bash
|
|
169
|
+
dbt compile --select fct_orders
|
|
170
|
+
```
|
|
171
|
+
2. **Execute Targeted Subgraph**:
|
|
172
|
+
```bash
|
|
173
|
+
dbt run --select +fct_orders # Run fct_orders and all upstream dependencies
|
|
174
|
+
dbt test --select stg_stripe+ # Test staging models and downstream marts
|
|
175
|
+
```
|
|
176
|
+
3. **Build Documentation & DAG Visualizer**:
|
|
177
|
+
```bash
|
|
178
|
+
dbt docs generate && dbt docs serve
|
|
179
|
+
```
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debezium-cdc
|
|
3
|
+
description: >-
|
|
4
|
+
Change Data Capture (CDC) streaming architectures using Debezium, PostgreSQL logical decoding (pgoutput), MySQL binlog replication, Kafka Connect, and transactional outbox patterns. Use when capturing real-time database mutations, synchronizing search indices, or avoiding dual-write race conditions.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Debezium Change Data Capture (CDC) Architecture
|
|
8
|
+
|
|
9
|
+
A production runbook for implementing distributed **Change Data Capture (CDC)** pipelines using Debezium, PostgreSQL/MySQL WAL replication, Kafka Connect, and event-driven data streaming.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 🎯 Core Architectural Principles
|
|
14
|
+
|
|
15
|
+
1. **Log-Based CDC Over Polling**: Reads write-ahead logs (WAL / binlog) directly at the storage engine level, incurring minimal CPU overhead and zero query locks on primary tables.
|
|
16
|
+
2. **Transactional Outbox Pattern**: Guarantees atomic database operations and event emission within a single ACID transaction without two-phase commit (2PC) or dual-write inconsistencies.
|
|
17
|
+
3. **Strict Ordering Guarantees**: Partitioning by primary key ensures sequential ordering per record entity across consumer groups.
|
|
18
|
+
4. **Idempotent Consumers**: Design downstream event processors with upsert semantics and revision monotonic version checks.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🏗️ Debezium PostgreSQL Connector Configuration
|
|
23
|
+
|
|
24
|
+
### 1. Database Prerequisite (`postgresql.conf`)
|
|
25
|
+
|
|
26
|
+
```ini
|
|
27
|
+
# Enable logical replication
|
|
28
|
+
wal_level = logical
|
|
29
|
+
max_wal_senders = 10
|
|
30
|
+
max_replication_slots = 10
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Kafka Connect Connector Payload (`debezium-postgres.json`)
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"name": "postgres-order-cdc",
|
|
38
|
+
"config": {
|
|
39
|
+
"connector.class": "io.debezium.connector.postgresql.PostgresConnector",
|
|
40
|
+
"tasks.max": "1",
|
|
41
|
+
"plugin.name": "pgoutput",
|
|
42
|
+
"database.hostname": "postgres.internal",
|
|
43
|
+
"database.port": "5432",
|
|
44
|
+
"database.user": "debezium_cdc",
|
|
45
|
+
"database.password": "${file:/secrets/db-credentials.properties:password}",
|
|
46
|
+
"database.dbname": "production_app",
|
|
47
|
+
"database.server.name": "prod_cdc",
|
|
48
|
+
"table.include.list": "public.orders,public.outbox_events",
|
|
49
|
+
"slot.name": "debezium_orders_slot",
|
|
50
|
+
"publication.name": "dbz_orders_pub",
|
|
51
|
+
"publication.autocreate.mode": "filtered",
|
|
52
|
+
"decimal.handling.mode": "double",
|
|
53
|
+
"tombstones.on.delete": "true",
|
|
54
|
+
"heartbeat.interval.ms": "5000"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Deploy to Kafka Connect cluster via REST API:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
curl -i -X POST -H "Accept:application/json" -H "Content-Type:application/json" \
|
|
63
|
+
http://kafka-connect.internal:8083/connectors/ \
|
|
64
|
+
-d @debezium-postgres.json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 📦 Transactional Outbox Pattern Implementation
|
|
70
|
+
|
|
71
|
+
### Step 1: Outbox Event Table Schema
|
|
72
|
+
|
|
73
|
+
```sql
|
|
74
|
+
CREATE TABLE outbox_events (
|
|
75
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
76
|
+
aggregate_type VARCHAR(64) NOT NULL,
|
|
77
|
+
aggregate_id VARCHAR(64) NOT NULL,
|
|
78
|
+
event_type VARCHAR(64) NOT NULL,
|
|
79
|
+
payload JSONB NOT NULL,
|
|
80
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
-- Index for optional periodic cleanup
|
|
84
|
+
CREATE INDEX idx_outbox_created_at ON outbox_events (created_at);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 2: Atomic Business Transaction (TypeScript / Node.js)
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { PoolClient } from 'pg';
|
|
91
|
+
|
|
92
|
+
interface OrderEvent {
|
|
93
|
+
orderId: string;
|
|
94
|
+
userId: string;
|
|
95
|
+
totalCents: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function createOrderWithOutbox(client: PoolClient, event: OrderEvent): Promise<void> {
|
|
99
|
+
try {
|
|
100
|
+
await client.query('BEGIN');
|
|
101
|
+
|
|
102
|
+
// 1. Mutate primary business state
|
|
103
|
+
const orderSql = `
|
|
104
|
+
INSERT INTO orders (id, user_id, total_cents, status)
|
|
105
|
+
VALUES ($1, $2, $3, 'CREATED')
|
|
106
|
+
`;
|
|
107
|
+
await client.query(orderSql, [event.orderId, event.userId, event.totalCents]);
|
|
108
|
+
|
|
109
|
+
// 2. Append event to outbox within same transaction
|
|
110
|
+
const outboxSql = `
|
|
111
|
+
INSERT INTO outbox_events (aggregate_type, aggregate_id, event_type, payload)
|
|
112
|
+
VALUES ('Order', $1, 'OrderCreated', $2)
|
|
113
|
+
`;
|
|
114
|
+
await client.query(outboxSql, [event.orderId, JSON.stringify(event)]);
|
|
115
|
+
|
|
116
|
+
await client.query('COMMIT');
|
|
117
|
+
} catch (error) {
|
|
118
|
+
await client.query('ROLLBACK');
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🔄 Debezium Event Payload Structure
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"before": null,
|
|
131
|
+
"after": {
|
|
132
|
+
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
133
|
+
"user_id": "usr_98231",
|
|
134
|
+
"total_cents": 12500,
|
|
135
|
+
"status": "CREATED"
|
|
136
|
+
},
|
|
137
|
+
"source": {
|
|
138
|
+
"version": "2.5.0.Final",
|
|
139
|
+
"connector": "postgresql",
|
|
140
|
+
"name": "prod_cdc",
|
|
141
|
+
"ts_ms": 1718000000000,
|
|
142
|
+
"snapshot": "false",
|
|
143
|
+
"db": "production_app",
|
|
144
|
+
"schema": "public",
|
|
145
|
+
"table": "orders",
|
|
146
|
+
"txId": 984120,
|
|
147
|
+
"lsn": 104857600
|
|
148
|
+
},
|
|
149
|
+
"op": "c",
|
|
150
|
+
"ts_ms": 1718000000100
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
* `op`: `"c"` (create), `"u"` (update), `"d"` (delete), `"r"` (snapshot read).
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 🛡️ Production Failure Modes & Operational Checklist
|
|
159
|
+
|
|
160
|
+
1. **Replication Slot Disk Exhaustion**:
|
|
161
|
+
- *Risk*: If Kafka Connect crashes, Postgres retains WAL logs indefinitely, filling disk.
|
|
162
|
+
- *Mitigation*: Set `max_slot_wal_keep_size = 50GB` in `postgresql.conf` to protect database uptime.
|
|
163
|
+
2. **Heartbeat Tuning**:
|
|
164
|
+
- Always configure `heartbeat.interval.ms` (e.g. 5000ms) to ensure LSN progression even during low-traffic periods.
|
|
165
|
+
3. **Schema Evolution Handling**:
|
|
166
|
+
- Use Schema Registry (Avro or Protobuf) with `BACKWARD` or `FULL` compatibility to avoid downstream deserialization failures.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: distributed-rate-limiting
|
|
3
|
+
description: >-
|
|
4
|
+
Architects, implements, and tunes distributed rate limiting using Redis sliding window logs, token buckets, tiered API quotas, RFC 6585 headers, and circuit breakers.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Distributed Rate Limiting & API Traffic Shaping
|
|
8
|
+
|
|
9
|
+
This skill guides the design, implementation, and tuning of distributed rate limiting systems across microservice clusters using Redis, token buckets, and sliding window algorithms.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ⚡ Algorithm Comparison Matrix
|
|
14
|
+
|
|
15
|
+
| Algorithm | Precision | Memory Usage | Burst Handling | Best For |
|
|
16
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
17
|
+
| **`Sliding Window Log`** | 100% Exact | High ($O(N)$ requests) | Excellent | Low-to-medium traffic sensitive endpoints (auth, payments). |
|
|
18
|
+
| **`Sliding Window Counter`**| ~99% Exact | Fixed ($O(1)$) | Smooth | High-scale public APIs and ingress gateways. |
|
|
19
|
+
| **`Token Bucket`** | High | Very Low ($O(1)$) | Controlled bursts | Standard microservice backpressuring and rate smoothing. |
|
|
20
|
+
| **`Fixed Window`** | Poor (2x burst at boundary) | Lowest | Prone to spikes | Simple non-critical background jobs only. |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 🔒 Atomic Sliding Window Rate Limiter (Redis Lua)
|
|
25
|
+
|
|
26
|
+
Atomic execution in Redis ensures no race conditions across distributed API instances:
|
|
27
|
+
|
|
28
|
+
```lua
|
|
29
|
+
-- sliding_window.lua
|
|
30
|
+
-- KEYS[1]: Rate limit key (e.g., "ratelimit:tenant_123:orders")
|
|
31
|
+
-- ARGV[1]: Current UNIX timestamp in milliseconds
|
|
32
|
+
-- ARGV[2]: Window size in milliseconds (e.g., 60000 for 1 minute)
|
|
33
|
+
-- ARGV[3]: Maximum allowed requests in window (e.g., 100)
|
|
34
|
+
|
|
35
|
+
local key = KEYS[1]
|
|
36
|
+
local now = tonumber(ARGV[1])
|
|
37
|
+
local window = tonumber(ARGV[2])
|
|
38
|
+
local limit = tonumber(ARGV[3])
|
|
39
|
+
local clearBefore = now - window
|
|
40
|
+
|
|
41
|
+
-- 1. Remove expired request timestamps outside the window
|
|
42
|
+
redis.call('ZREMRANGEBYSCORE', key, 0, clearBefore)
|
|
43
|
+
|
|
44
|
+
-- 2. Count requests currently in the sliding window
|
|
45
|
+
local currentRequests = redis.call('ZCARD', key)
|
|
46
|
+
|
|
47
|
+
if currentRequests < limit then
|
|
48
|
+
-- 3. Register current request with unique timestamp as score and member
|
|
49
|
+
redis.call('ZADD', key, now, now .. '-' .. math.random(1000, 9999))
|
|
50
|
+
redis.call('PEXPIRE', key, window)
|
|
51
|
+
return { 1, limit - currentRequests - 1, 0 } -- [Allowed: 1, Remaining, RetryAfter: 0]
|
|
52
|
+
else
|
|
53
|
+
-- 4. Calculate time until oldest entry expires
|
|
54
|
+
local oldest = redis.call('ZRANGE', key, 0, 0, 'WITHSCORES')
|
|
55
|
+
local retryAfter = math.ceil((tonumber(oldest[2]) + window - now) / 1000)
|
|
56
|
+
return { 0, 0, math.max(1, retryAfter) } -- [Allowed: 0, Remaining: 0, RetryAfter]
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🌐 Express / Fastify / Node.js Middleware Implementation
|
|
63
|
+
|
|
64
|
+
Wrap Redis Lua execution into production middleware with RFC 6585 compliance:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Request, Response, NextFunction } from 'express';
|
|
68
|
+
import Redis from 'ioredis';
|
|
69
|
+
|
|
70
|
+
const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');
|
|
71
|
+
|
|
72
|
+
export function createRateLimiter(options: { windowMs: number; max: number; keyGenerator?: (req: Request) => string }) {
|
|
73
|
+
return async (req: Request, res: Response, next: NextFunction) => {
|
|
74
|
+
const key = options.keyGenerator ? options.keyGenerator(req) : `ratelimit:${req.ip}`;
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const [allowed, remaining, retryAfter] = await redis.eval(
|
|
79
|
+
SLIDING_WINDOW_LUA_SCRIPT,
|
|
80
|
+
1,
|
|
81
|
+
key,
|
|
82
|
+
now.toString(),
|
|
83
|
+
options.windowMs.toString(),
|
|
84
|
+
options.max.toString()
|
|
85
|
+
) as [number, number, number];
|
|
86
|
+
|
|
87
|
+
// Set standard RFC 6585 RateLimit response headers
|
|
88
|
+
res.setHeader('X-RateLimit-Limit', options.max);
|
|
89
|
+
res.setHeader('X-RateLimit-Remaining', remaining);
|
|
90
|
+
res.setHeader('X-RateLimit-Reset', Math.ceil((now + options.windowMs) / 1000));
|
|
91
|
+
|
|
92
|
+
if (allowed === 1) {
|
|
93
|
+
return next();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
res.setHeader('Retry-After', retryAfter);
|
|
97
|
+
return res.status(429).json({
|
|
98
|
+
error: 'Too Many Requests',
|
|
99
|
+
message: `Rate limit exceeded. Please retry after ${retryAfter} seconds.`,
|
|
100
|
+
retryAfter,
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
// Circuit Breaker: Fail-Open for high availability if Redis degrades
|
|
104
|
+
console.error('RateLimiter Redis failure, failing open:', err);
|
|
105
|
+
return next();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 🎯 Tiered Multi-Dimensional Quotas
|
|
114
|
+
|
|
115
|
+
Configure tiered rate limits based on authenticated API keys and endpoint criticality:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
export function getTierLimits(tier: 'free' | 'pro' | 'enterprise') {
|
|
119
|
+
switch (tier) {
|
|
120
|
+
case 'enterprise':
|
|
121
|
+
return { windowMs: 60_000, max: 10_000 };
|
|
122
|
+
case 'pro':
|
|
123
|
+
return { windowMs: 60_000, max: 1_200 };
|
|
124
|
+
case 'free':
|
|
125
|
+
default:
|
|
126
|
+
return { windowMs: 60_000, max: 60 };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 📋 Architectural Rules & Best Practices
|
|
134
|
+
|
|
135
|
+
1. **Atomic Scripts**: Never implement rate limiters with separated `GET` and `INCR` commands; always execute atomic Lua scripts or Redis Transactions (`MULTI/EXEC`) to prevent race conditions under load.
|
|
136
|
+
2. **Fail-Open Strategy**: For user-facing critical flows, catch Redis connection timeouts and fail-open with alerting rather than causing total service outages.
|
|
137
|
+
3. **Multi-Dimensional Keys**: Combine client identity with endpoint routes (e.g. `tenant:123:POST:/api/v1/checkout`) to prevent denial of service on high-cost endpoints.
|
|
138
|
+
4. **Header Standards**: Always emit `Retry-After`, `X-RateLimit-Limit`, and `X-RateLimit-Remaining` to allow compliant clients to back off exponentially.
|