mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-20 06:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
## Routing Patterns
|
||||
|
||||
### Session Affinity (Stateful)
|
||||
|
||||
```typescript
|
||||
export class SessionBackend extends Container {
|
||||
defaultPort = 3000;
|
||||
sleepAfter = "30m";
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
const sessionId = request.headers.get("X-Session-ID") || crypto.randomUUID();
|
||||
const container = env.SESSION_BACKEND.getByName(sessionId);
|
||||
await container.startAndWaitForPorts();
|
||||
return container.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Use:** User sessions, WebSocket, stateful games, per-user caching.
|
||||
|
||||
### Load Balancing (Stateless)
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
const container = env.STATELESS_API.getRandom();
|
||||
await container.startAndWaitForPorts();
|
||||
return container.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Use:** Stateless HTTP APIs, CPU-intensive work, read-only queries.
|
||||
|
||||
### Singleton Pattern
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
const container = env.GLOBAL_SERVICE.getByName("singleton");
|
||||
await container.startAndWaitForPorts();
|
||||
return container.fetch(request);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Use:** Global cache, centralized coordinator, single source of truth.
|
||||
|
||||
## WebSocket Forwarding
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
if (request.headers.get("Upgrade") === "websocket") {
|
||||
const sessionId = request.headers.get("X-Session-ID") || crypto.randomUUID();
|
||||
const container = env.WS_BACKEND.getByName(sessionId);
|
||||
await container.startAndWaitForPorts();
|
||||
|
||||
// ⚠️ MUST use fetch(), not containerFetch()
|
||||
return container.fetch(request);
|
||||
}
|
||||
return new Response("Not a WebSocket request", { status: 400 });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**⚠️ Critical:** Always use `fetch()` for WebSocket.
|
||||
|
||||
## Graceful Shutdown
|
||||
|
||||
```typescript
|
||||
export class GracefulContainer extends Container {
|
||||
private connections = new Set<WebSocket>();
|
||||
|
||||
onStop() {
|
||||
// SIGTERM received, 15 minutes until SIGKILL
|
||||
for (const ws of this.connections) {
|
||||
ws.close(1001, "Server shutting down");
|
||||
}
|
||||
this.ctx.storage.put("shutdown-time", Date.now());
|
||||
}
|
||||
|
||||
onActivityExpired(): boolean {
|
||||
return this.connections.size > 0; // Keep alive if connections
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Concurrent Request Handling
|
||||
|
||||
```typescript
|
||||
export class SafeContainer extends Container {
|
||||
private initialized = false;
|
||||
|
||||
async fetch(request: Request) {
|
||||
await this.ctx.blockConcurrencyWhile(async () => {
|
||||
if (!this.initialized) {
|
||||
await this.startAndWaitForPorts();
|
||||
this.initialized = true;
|
||||
}
|
||||
});
|
||||
return super.fetch(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use:** One-time initialization, preventing concurrent startup.
|
||||
|
||||
## Activity Timeout Renewal
|
||||
|
||||
```typescript
|
||||
export class LongRunningContainer extends Container {
|
||||
sleepAfter = "5m";
|
||||
|
||||
async processLongJob(data: unknown) {
|
||||
const interval = setInterval(() => {
|
||||
this.ctx.storage.put("keepalive", Date.now());
|
||||
}, 60000);
|
||||
|
||||
try {
|
||||
await this.doLongWork(data);
|
||||
} finally {
|
||||
clearInterval(interval);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use:** Long operations exceeding `sleepAfter`.
|
||||
|
||||
## Multiple Port Routing
|
||||
|
||||
```typescript
|
||||
export class MultiPortContainer extends Container {
|
||||
requiredPorts = [8080, 8081, 9090];
|
||||
|
||||
async fetch(request: Request) {
|
||||
const path = new URL(request.url).pathname;
|
||||
if (path.startsWith("/grpc")) this.switchPort(8081);
|
||||
else if (path.startsWith("/metrics")) this.switchPort(9090);
|
||||
return super.fetch(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use:** Multi-protocol services (HTTP + gRPC), separate metrics endpoints.
|
||||
|
||||
## Workflow Integration
|
||||
|
||||
```typescript
|
||||
import { WorkflowEntrypoint } from "cloudflare:workers";
|
||||
|
||||
export class ProcessingWorkflow extends WorkflowEntrypoint {
|
||||
async run(event, step) {
|
||||
const container = this.env.PROCESSOR.getByName(event.payload.jobId);
|
||||
|
||||
await step.do("start", async () => {
|
||||
await container.startAndWaitForPorts();
|
||||
});
|
||||
|
||||
const result = await step.do("process", async () => {
|
||||
return container.fetch("/process", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(event.payload.data)
|
||||
}).then(r => r.json());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Use:** Orchestrating multi-step container operations, durable execution.
|
||||
|
||||
## Queue Consumer Integration
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async queue(batch, env) {
|
||||
for (const msg of batch.messages) {
|
||||
try {
|
||||
const container = env.PROCESSOR.getByName(msg.body.jobId);
|
||||
await container.startAndWaitForPorts();
|
||||
|
||||
const response = await container.fetch("/process", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(msg.body)
|
||||
});
|
||||
|
||||
response.ok ? msg.ack() : msg.retry();
|
||||
} catch (err) {
|
||||
console.error("Queue processing error:", err);
|
||||
msg.retry();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Use:** Asynchronous job processing, batch operations, event-driven execution.
|
||||
Reference in New Issue
Block a user