AI 资讯
Fair Queue for a Shared Free AI Server: 5-Dev Postmortem
Five independent clients on one free AI server will produce 429s and a thundering herd unless you add a fair queue. We fixed it with a client-side asyncio queue that capped concurrency at two, prioritized interactive work, and dropped 429s from 23 to 0 on a 100-request mixed workload. Disclosure: This article was prepared as part of MonkeyCode's product outreach. What Failed When Five Developers Shared One Server We shared one MonkeyCode free server for code review and refactoring. Each of us ran our own scripts. Nobody coordinated. The first symptom was latency: requests that took two seconds started taking thirty. Then came the 429s. Then came the retries. Retries made everything worse. The server spent more time rejecting requests than answering them. The timeline compressed quickly: Day 1: two developers, no issues Day 3: four developers, latency doubles Day 5: five developers, 429s appear Day 6: retries cause a thundering herd Day 7: the team stops using the server The root cause was not the server. It was the absence of coordination. Five independent clients hammered one endpoint. Each client assumed it was the only user. The server had no way to prioritize. HTTP 429 is the standard “too many requests” signal; we treated it as a retry cue instead of backpressure. That is how a shared free endpoint turns into a retry storm. The deeper problem was architectural. Each of us built a separate integration. Each integration had its own retry logic. Under load those retries multiplied. The server received about five times the intended traffic, not because we needed five times the work, but because five clients were guessing independently. Contrast the two modes we actually ran: Uncoordinated: five scripts, five retry loops, unbounded in-flight calls, no shared view of queue depth. Coordinated: one process, one priority heap, two in-flight calls, explicit rejection when the queue is full. The first mode failed in a week. The second mode is what we shipped. How We Built
AI 资讯
async/await without the pitfalls
async/await without the pitfalls Async/await is the bread and butter of modern JavaScript. It makes asynchronous code look synchronous, which is great for readability. But it comes with its own set of footguns that can bite you in production. Here's how to avoid them. Pitfall 1: Forgetting await in a loop You might write something like this, expecting each request to finish before the next starts: async function fetchAll ( urls ) { const results = []; for ( const url of urls ) { const res = await fetch ( url ); // this is fine, but see below results . push ( await res . json ()); } return results ; } That's actually correct. The issue arises when you forget await inside a .map() or .forEach() : // Wrong: map returns an array of promises, not data const data = urls . map ( async ( url ) => { const res = await fetch ( url ); return res . json (); }); // data is now an array of promises, not the JSON data async functions always return a promise. So if you use map with an async callback, you get an array of promises. To fix it, use Promise.all : const data = await Promise . all ( urls . map ( async ( url ) => { const res = await fetch ( url ); return res . json (); })); But beware: Promise.all fails fast. If one request fails, the whole thing rejects. If you need to handle failures individually, use Promise.allSettled instead. Pitfall 2: Swallowing errors silently A common mistake is to catch an error and do nothing, which makes debugging a nightmare: try { const data = await fetchData (); // process data } catch ( error ) { // do nothing? bad! } Always at least log the error. Even better, handle it gracefully or rethrow it: try { const data = await fetchData (); } catch ( error ) { console . error ( ' Failed to fetch data: ' , error ); throw error ; // rethrow if you want the caller to handle it } If you're using async/await , unhandled promise rejections can crash your app in Node.js. Always have a catch or a global handler. Pitfall 3: Sequential execution when you ne
AI 资讯
Python PostgreSQL with asyncpg: Async Database Operations
Python PostgreSQL with asyncpg: Async Database Operations asyncpg is the fastest PostgreSQL driver for Python — pure asyncio, no thread overhead, and up to 3× faster than psycopg2 on typical workloads. It is the go-to choice for any async Python backend. Installation pip install asyncpg # PostgreSQL server must already be running Connect and Create a Pool import asyncio import asyncpg from datetime import datetime DATABASE_URL = " postgresql://user:password@localhost:5432/mydb " async def create_pool () -> asyncpg . Pool : pool = await asyncpg . create_pool ( DATABASE_URL , min_size = 2 , max_size = 10 , command_timeout = 30 , server_settings = { " application_name " : " myapp " }, ) print ( " Pool created. " ) return pool Schema Setup CREATE_TABLES = """ CREATE TABLE IF NOT EXISTS users ( id BIGSERIAL PRIMARY KEY, username TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS posts ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, title TEXT NOT NULL, body TEXT NOT NULL DEFAULT '' , published BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_posts_user ON posts(user_id); CREATE INDEX IF NOT EXISTS idx_posts_created ON posts(created_at DESC); """ async def setup_schema ( pool : asyncpg . Pool ) -> None : async with pool . acquire () as conn : await conn . execute ( CREATE_TABLES ) print ( " Schema ready. " ) INSERT — Adding Records async def create_user ( pool : asyncpg . Pool , username : str , email : str ) -> int : async with pool . acquire () as conn : row = await conn . fetchrow ( """ INSERT INTO users (username, email) VALUES ($1, $2) ON CONFLICT (username) DO UPDATE SET email = EXCLUDED.email RETURNING id, created_at """ , username , email , ) return row [ " id " ] async def create_post ( pool : asyncpg . Pool , user_id : int , title : str , body : str , published : bool = False , ) ->
开源项目
Five tabs open, one refresh token — the race nobody noticed
A user reports that they keep getting logged out. Not immediately — after a while, randomly, always...
AI 资讯
Async Error Handling — Async Route
Async route: vì sao Promise reject trong Express 4 không tới error middleware, và cách Fastify tự bắt lỗi Một async route handler trông giống một handler bình thường, nhưng cơ chế truyền lỗi của nó khác hẳn. Express 4 bọc lời gọi handler bằng try/catch đồng bộ; một hàm async return về một Promise trước khi Promise đó settle, nên try/catch đồng bộ không bắt được rejection. Kết quả: lỗi rơi ra khỏi handler dưới dạng unhandledRejection , không tới error middleware, không được gửi thành 500 JSON , và từ Node 15 mặc định làm process crash. Fastify đi hướng khác — pipeline hook tự await handler, rejection được framework bắt và chuyển tới setErrorHandler . Hiểu điểm khác biệt này giải thích vì sao cùng một dòng code await db.query(...) lại có hai hệ quả production hoàn toàn khác nhau giữa hai framework. Cơ chế hoạt động Trong Express 4, Layer.handle_request gọi handler đồng bộ trong một khối try/catch . Nếu handler ném exception đồng bộ, catch được, forward qua next(err) . Nhưng handler async không ném — nó return về một Promise. Đến khi Promise reject, control đã ra khỏi khối try/catch từ lâu: // Đây là bản giản lược của cách Express 4 gọi handler: try { const ret = handler ( req , res , next ) // với async handler, ret là Promise (đã pending) // handler đã "return" — try/catch không còn hiệu lực với reject xảy ra sau } catch ( err ) { next ( err ) // chỉ chạy khi handler ném đồng bộ } Vì Express 4 không await ret và cũng không ret.catch(next) , một Promise reject không có bên nào bắt. Node emit unhandledRejection ở tầng process. Cách sửa chuẩn là wrapper: // asyncHandler: bọc handler async, forward reject vào next(err) export const asyncHandler = ( fn ) => ( req , res , next ) => Promise . resolve ( fn ( req , res , next )). catch ( next ) app . get ( ' /orders/:id ' , asyncHandler ( async ( req , res ) => { const order = await db . orders . findById ( req . params . id ) if ( ! order ) { res . status ( 404 ). json ({ error : ' not_found ' }); return } res . json ( order
AI 资讯
Message Queue — Async Processing
Async processing qua message queue: vì sao đẩy việc nặng ra khỏi request path, và cái giá phải trả bằng eventual consistency Async processing là mô hình tách một request thành hai giai đoạn: request handler nhận việc, xác nhận với client, rồi giao phần xử lý thật cho một worker chạy ngoài request path — thường qua một message queue (RabbitMQ, AWS SQS, Kafka, Redis Streams, hoặc queue trên nền Redis như BullMQ/Sidekiq). Lý do dev gặp nó trong việc thật rất cụ thể: một endpoint gọi payment provider mất 3s, gửi email confirm mất 1s, resize ảnh mất 5s — nếu làm tuần tự trong request, p99 latency của endpoint là tổng các con số đó, và một downstream chậm hoặc chết đủ để làm timeout hết thread pool của app server. Đẩy vào queue thì request trả về trong vài chục ms; nhưng đổi lại, cái "xong" mà client thấy không còn nghĩa là việc đã thực sự hoàn thành. Cơ chế hoạt động Ba thành phần: producer (thường là API server) đóng gói việc thành message rồi publish vào broker; broker (RabbitMQ/SQS/Kafka…) giữ message trong queue có persistence tuỳ cấu hình; consumer/worker poll hoặc được push message, xử lý, rồi ack để broker biết xoá. Nếu worker chết trước khi ack, broker redeliver — đây là gốc của semantic at-least-once : mỗi message được giao ít nhất một lần, có thể nhiều lần. Exactly-once trong hệ phân tán chỉ đạt được ở lớp application bằng cách consumer viết idempotent, không phải bằng cấu hình broker. Ví dụ với RabbitMQ + Node ( amqplib ): // producer — trong HTTP handler const ch = await conn . createConfirmChannel () await ch . assertQueue ( ' image.resize ' , { durable : true }) app . post ( ' /upload ' , async ( req , res ) => { const jobId = crypto . randomUUID () const payload = Buffer . from ( JSON . stringify ({ jobId , s3Key : req . body . key })) await ch . sendToQueue ( ' image.resize ' , payload , { persistent : true , // ghi xuống disk, sống sót broker restart messageId : jobId , // để consumer dedupe contentType : ' application/json ' , }) // đợi broker confirm đ
AI 资讯
Synchronous vs asynchronous in .NET core - how decide
Rule of thumb If your action waits on something external , make it async . If it’s instant CPU , keep it sync ; for expensive CPU , offload . The core idea Async shines for I/O-bound work (DB calls, HTTP calls, queues, files). It frees the request thread while waiting, so the server can serve more requests with the same thread pool . Sync is fine for trivial, short CPU work (formatting, small calculations) where you’re not awaiting anything and the handler returns in a few milliseconds. When to choose async You call EF Core ( SaveChangesAsync , ToListAsync ), HttpClient , Azure SDK ( ServiceBusClient , BlobClient ), file I/O, or any API with Async methods. You expect latency from a dependency (tens–hundreds of ms). You need cancellation and timeouts (propagate HttpContext.RequestAborted ). When sync is acceptable The action is pure CPU and trivial (e.g., quick math, mapping, input validation) and returns immediately. There are no I/O waits and no benefit from freeing the thread. If it’s CPU-heavy (image processing, big JSON transforms), do not just make it async—offload to a background queue/worker or a separate compute service. Async won’t make CPU faster. Pitfalls to avoid Don’t block async : never use .Result / .Wait() on Tasks (deadlocks/thread-pool starvation). Async all the way down : if the controller is async, downstream calls should be too. Don’t fake async : returning Task.Run around synchronous I/O just burns threads. Keep concurrency bounded when fanning out to multiple I/O calls. Mini decision checklist Any I/O? → Use async (end-to-end). Pure CPU? Tiny (≤ a few ms) → Sync is fine. Heavy/variable → Offload to background worker; controller returns 202/Location or uses a queue. ASP.NET Core examples Async (I/O-bound) — recommended [ ApiController ] [ Route ( "orders" )] public class OrdersController : ControllerBase { private readonly OrdersDbContext _db ; private readonly HttpClient _http ; public OrdersController ( OrdersDbContext db , IHttpClientFactory
开发者
Article: Beyond CLEAN and MVP: Architecting an Offline-first Reactive Data Layer in Android
With the Reactive Data Layer Architecture (RDLA), you establish a clear boundary between public data APIs and private, framework-specific data-source implementations. Your presentation layer operates in a purely reactive manner, observing data changes rather than procedurally querying them. RDLA also simplifies testing by encouraging you to program to interfaces and use clean seeding patterns. By Mervyn Anthony
AI 资讯
The Polling API Is the Most Underrated RFC PHP Has Shipped in Years
While most of the community spent the spring arguing about generics, a different RFC slipped into PHP 8.6 with almost none of the attention it deserved. No long Twitter threads. No blog posts dissecting the implications. Just a quiet vote that closed on the third of June with 33 in favour, one against, and four abstaining, from a list of names that includes the Composer author, the FrankenPHP creator, and a healthy chunk of the people who actually maintain the async libraries you depend on. That RFC is the Polling API, authored by Jakub Zelenka as part of his ongoing stream evolution work. I want to make the case that it is the most consequential thing to land in PHP in years, and that the reason nobody is talking about it is exactly the reason it matters. The problem you have been quietly working around If you have ever written anything that needs to watch more than one socket at a time, you have met stream_select() . It is the only I/O multiplexing primitive PHP has shipped for most of its life, and it is built on the select() system call from 1983. It works. It also carries a set of limitations that every async library author has had to engineer around. The first is the file descriptor ceiling. The current implementation caps out at around 1024 descriptors on most systems, which is fine until the moment it very much is not. The second is the complexity. select() is O(n): it scans every descriptor you hand it on every single call, so performance degrades as you add connections. The third is that it gives you no access to the mechanisms the rest of the world has been using for two decades. There is no epoll on Linux, no kqueue on BSD or macOS, no event ports on Solaris. There is no edge-triggered mode and no one-shot mode. This is why every serious async runtime reaches past select() . Node, Nginx, Go's net package, Rust's Tokio: they all sit on epoll or kqueue, because that is the foundation high-concurrency networking is built on. PHP was the last major runtime w
AI 资讯
From ThreadPoolExecutor to httpx AsyncClient: True Async Refactoring
Published on : 2026-06-06 Reading time : 6 min Tags : #python #async #performance #optimization The Problem: Fake Async The supabase-async library claimed to be async but actually wrapped synchronous calls with ThreadPoolExecutor: # ❌ Fake async (old code) class SupabaseAsync : def __init__ ( self ): self . _executor = ThreadPoolExecutor ( max_workers = 3 ) async def select ( self , table : str ): loop = asyncio . get_event_loop () r = await loop . run_in_executor ( self . _executor , lambda : requests . get ( url ) # Sync call wrapped as async ) return r . json () Problems : Max 3 concurrent requests (not scalable) Thread overhead per request High memory usage No connection pooling Solution: httpx AsyncClient Use true async HTTP with httpx: # ✅ Real async (new code) import httpx class SupabaseAsync : def __init__ ( self ): self . _client : Optional [ httpx . AsyncClient ] = None async def _get_client ( self ) -> httpx . AsyncClient : if self . _client is None : self . _client = httpx . AsyncClient ( headers = self . _headers , timeout = 30 , limits = httpx . Limits ( max_connections = 10 ) ) return self . _client async def select ( self , table : str ): client = await self . _get_client () r = await client . get ( f " { self . _base } / { table } " ) r . raise_for_status () return r . json () Performance Gains Metric ThreadPoolExecutor(3) httpx(10) Max concurrent 3 requests 10 requests Avg response 450ms 150ms Memory usage 250MB 180MB Throughput 6.7 req/s 20 req/s Real benchmark : 100 concurrent requests ThreadPoolExecutor: 15 seconds httpx AsyncClient: 5 seconds 3x faster ⚡ Migration Steps 1. Client Initialization with Lazy Loading async def _get_client ( self ) -> httpx . AsyncClient : if self . _client is None : self . _client = httpx . AsyncClient ( headers = self . _headers , timeout = 30 , limits = httpx . Limits ( max_connections = 10 , max_keepalive_connections = 5 ) ) return self . _client 2. HTTP Methods (GET, POST, etc.) async def _request ( self , metho
AI 资讯
supabase-async: ThreadPoolExecutor에서 httpx AsyncClient로 리팩토링
Published on : 2026-06-06 Reading time : 6 min Tags : #python #async #performance #optimization 문제: 거짓 비동기 supabase-async 라이브러리는 이름은 async이지만, 실제로는 ThreadPoolExecutor로 동기 호출을 래핑하고 있었습니다. # ❌ 거짓 비동기 (기존 코드) class SupabaseAsync : def __init__ ( self ): self . _executor = ThreadPoolExecutor ( max_workers = 3 ) async def select ( self , table : str ): loop = asyncio . get_event_loop () r = await loop . run_in_executor ( self . _executor , lambda : requests . get ( url ) # 동기 호출을 async로 포장 ) return r . json () 문제점 : 최대 3개 동시 요청만 가능 (동시성 부족) 스레드 오버헤드 (각 요청마다 스레드 생성) 높은 메모리 사용량 해결책: httpx AsyncClient 진정한 비동기 HTTP 클라이언트인 httpx를 사용합니다. # ✅ 진정한 비동기 (수정된 코드) import httpx class SupabaseAsync : def __init__ ( self ): self . _client : Optional [ httpx . AsyncClient ] = None async def _get_client ( self ) -> httpx . AsyncClient : if self . _client is None : self . _client = httpx . AsyncClient ( headers = self . _headers , timeout = 30 , limits = httpx . Limits ( max_connections = 10 ) ) return self . _client async def select ( self , table : str ): client = await self . _get_client () r = await client . get ( f " { self . _base } / { table } " ) r . raise_for_status () return r . json () 성능 개선 동시성 비교 지표 ThreadPoolExecutor(3) httpx(10) 최대 동시 요청 3개 10개 평균 응답 시간 450ms 150ms 메모리 사용량 250MB 180MB 초당 처리량 6.7 req/s 20 req/s 벤치마크 # 100개 동시 요청 처리 시간 ThreadPoolExecutor : 15 초 httpx AsyncClient : 5 초 → 3 배 빠름 마이그레이션 단계 1. 클라이언트 초기화 async def _get_client ( self ) -> httpx . AsyncClient : if self . _client is None : self . _client = httpx . AsyncClient ( headers = self . _headers , timeout = 30 , limits = httpx . Limits ( max_connections = 10 , max_keepalive_connections = 5 ) ) return self . _client 2. 요청 메서드 async def _request ( self , method : str , url : str , ** kwargs ): client = await self . _get_client () if method == " GET " : return await client . get ( url , ** kwargs ) elif method == " POST " : return await client . post ( url , ** kwargs ) # ... 3. Context Manager 지원 async def clos