慣性聚合 高效追讀感興趣之博客、新聞、科技資訊
閱原文 以慣性聚合開啟

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
双录簿中一应力测试所现之竞态条件,及其如何得解
Do Pham Dinh · 2026-05-24 · via DEV Community

吾正营构之账簿服务,乃Java 21 / Spring Boot 3.5 / PostgreSQL之双录电子钱包账簿也。栖于Render之上早岁吾撰一压力测试,发五十次传输于其上。同一账户 顿觉书卷永无朽坏之理。其色转赤——赤色之变,乃吾筑此间所获至要之识。

是文遍述其理:何以钱簿恒守平衡? 缓存之由。固无谓,缓存所召之读改写竞态,吾如何察之,其修(乐观锁+有界重试),其基准(证乐观锁胜悲观锁),及幂等须与重试相合,使网络小歇不致重费。

设备:一账本,及其设缓存之由

真源乃一复式、仅增之账册 表 (ADR-0005)。每项金钱之运作,必书至少一平衡之 DEBIT/CREDIT 对,其 Σ DEBIT == Σ CREDIT,而 ledger_entries 乃唯增之——无 UPDATE,亦无 DELETE。谬误既生,则书一 以正之。条目,非由修订历史而成。此乃 Stripe、Modern Treasury、Formance皆所采用之模,亦即赋予汝可信赖之审计轨迹者也。

然"X之账户余额何在?"非也。SUM于账户所历之每一项,吾恒存之。缓存:accounts.balance乃成实物也Σ此账目之记录,已更新于此同时交易盖条目本身(ADR-0006)即真也。条目为真,余额乃派生之读缓存,恒为O(1)。

此缓存正为竞态而生。

竞渡

二请同时扣同一账户:

R1: read balance $500 (enough)      R2: read balance $500 (enough)
R1: commit −$300 → $200             R2: commit −$400 → −$200   ← overdraft / lost update

入全景模式 出全屏模式

二者皆读$500,皆以为足,复书己见,欲新定其衡。一默书,遂覆他书:此乃失序之更,衡不复与簿记相应矣。

所陷者,乃以为数据库自能禁之。非也。PostgreSQL之常态隔离,READ COMMITTED,惟保尔不读未提交之数据——然于两事务各读后写同列而并行者,无能为也。读改写之竞,直穿而过之。

察之:压力之试

此乃现此谬之试。先注一户,再发之。N = 50与之同时迁出,而后稽核账册:

AtomicInteger successes = new AtomicInteger();
CountDownLatch start = new CountDownLatch(1);
ExecutorService pool = Executors.newFixedThreadPool(16);
for (int i = 0; i < N; i++) {
    pool.submit(() -> {
        start.await();                       // line them all up...
        int code = post("/transfers", from, to, AMOUNT);   // ...then fire at once
        if (code == 201) successes.incrementAndGet();
    });
}
start.countDown();
// after all complete:
assertThat(balanceCache(from)).isEqualTo(ledgerBalance(from));   // cache == Σ entries
assertThat(balanceCache(from)).isGreaterThanOrEqualTo(0);        // no overdraft
assertThat(balanceCache(to)).isEqualTo((long) ok * AMOUNT);      // exact accounting

全屏模式 退出全屏模式

此断言有意 与时序无关 — 适用于 任何成败之分,盖因彼较缓存于账实,而非较之固定预期之数。此乃使试为稳定之回归守卫,而非易变者也。

吾以实验证之,其虫实然:与@Version柱已去八成有五之缓存更新俱失 诸此断言赤色矣——缓存之余额远离诸条目之总和。缓存与真谛相悖,此乃货币体系之全局所在。

整之策,其一:乐观锁

accounts 既存 version BIGINT 之列,盖因账户乃聚合/锁之界域(ADR-0010)。映射为 JPA @Version,则每写余额皆为比较并置:

UPDATE accounts SET balance = ?, version = version + 1
 WHERE id = ? AND version = ?

入全屏模式 出全屏模式

有二同時寫者,皆載版本7。先成者立之為8。後者之UPDATE ... WHERE version = 7,今與零行相合,而Hibernate於成時發OptimisticLockingFailureException。失更新今不可得:非復默然覆壞,敗者為 言于 ,曰其失矣。

其要义在:此乃 之察,非阻 也。无读者尝候锁。于账簿——其收支之数,读远胜于写——此甚要也。

其补,其二:限重试

独察不足。以@Version 既置而不再试,则压力测试虽止于数据之毁,然巨量之传输今复于冲突——虽正,然体验实劣。故负者当再试之。

重试之助,居 @Transactional,而此置,即其要义所在:

public <T> T execute(Supplier<T> operation) {
    for (int attempt = 1; ; attempt++) {
        try {
            return operation.get();           // a FRESH transaction each attempt
        } catch (OptimisticLockingFailureException e) {
            if (attempt >= maxAttempts) throw new ConcurrencyConflictException();
            sleep(backoffWithFullJitter(attempt));   // 25–200 ms, capped
        }
    }
}

入全屏模式 出全屏模式

每试皆为全新交易,重载其现行版 — 失败交易内重试,徒劳于陈旧之版。默认:五试,指数退避,配全抖动(免雷群复撞),竭尽则净409 Conflict — 终不复500.

有微理焉,使此于中度负荷下可证其必终:第k位提交者,唯能败于异时之早先提交者,故其所需至多k之尝试。四者并行,五次之限,皆可必然成功——无不可靠之测试。真热账户(并行者多于尝试之限)现于409,此乃诚实之缓冲,非隐秘之腐败.

乐观与悲观:权衡之择

显见之替代,乃悲观锁定——SELECT ... FOR UPDATE 莫触行而先锁之,故作者二但待。不重试,思之易明。然何故尚乐观?

吾不欲以感气争之,乃作一测试 (TransferConcurrencyBenchmark),使二策皆行 同此移转之理,五十并作之作者,对一实 PostgreSQL:

境况 乐观重试 悲观FOR UPDATE
争端少五十字之离析户牖 三十四毫秒· 各半可也 ·零重试徒劳 三十一毫秒 · 五十比五十可
高争之局五十次转移→一热争 毫秒七三一 · 五十比五十無誤 · 毫秒一百八十五重試浪費 毫秒三百五十八 · · 五十比五十無誤

讀數之義:

  • 爭奪甚微乃常態,然平分秋色(毫秒三百四十四對三百一十一)—然樂觀之法浪費重試之次,至關要者,未嘗阻滯讀取之途此乃重读账簿之决断也。
  • 独处炎炎之席,悲观者速约倍之。(358與731毫秒對比)且不浪費,而樂觀者則在碰撞與回退上多耗185次嘗試(≈4.7倍工作量)。然悲觀者之勝,正在此處。序列化并阻塞读取— 事我所欲避者 — 而实非也热户,但仅作排队处理。

故其判语为乐观+重试,而基准之价值非“乐观速”,(实则争用时非然)——乃在185虚耗之重试,可量真热户之阈限,此真热户(思:每充值扣款共享)SYSTEM_FUNDING此行需真晋升:异步排队或子账户分片,非将全系统转为悲观锁.

具备幂等性的组合

另有途径致重试实为使事更甚若不慎之。客之连接于服务器既决,必重试其全HTTP请求——今汝之患,乃贻误传贻二次。冲突重试与网络微颤重试,实为异事,其一之方,不可损其二也。

故二金端皆需一Idempotency-Key首部(ADR-0012,Stripe之式)。使其得兼并行无碍者,其法若何。主张首

INSERT INTO idempotency_keys (key, status) VALUES (?, 'PENDING')
ON CONFLICT (key) DO NOTHING;     -- committed immediately, before business logic

进入全屏模式 退出全屏模式

彼原子插入者,乃序列之枢。谁主其争,则操其事;若并时之请,同其键者,睹既成之PENDING行,遂得409 (空中) 而非重行。既成之钥复演所储应答;复用之钥遇 异体之 ,则得 422 (此乃客户契约违犯,刻意与 409 冲突码相别)。

此故能与前者之重试清整相合:乐观锁之重试位 后。 密钥已据为己有。凡内里尝试,皆在一已据之密钥下进行,故于客全不可见,亦绝无再发之可能。冲突重试与请求幂等之栈,非争斗,乃相济也。

下一步当求

缓存虽速,然偏移(此乃弊,偏败也)。故须定时而动。调和之职,每自不変之記復得平準,有差異則警之——不自行正,必待吏員更錄。此記錄唯增無減,故真實可永復。

复有热户之限,显于185次重试,此乃次级之真障也:若一列确有争用,则当以异步发布或分片该户,视重试之率,为知已越雷池之信。


要旨所在:于财制之中,缓存与账簿相悖。者,事之败也,而@Version之数据库,不能阻君成之。__JHSNS_SEG_1b425c00_170__之比较并置,使失更不可能;有界重试,加抖动,则寻常负荷下,其隐不易察。基准测试,可示所费之价,及上限所在;而幂等性,则保重试——自各层——不致成倍消费。

代码: github.com/xidoke/ledger-service — 此 并发之模 文及 ADRs 0005, 0006, 0011, 0012 更深入焉。 现场演示: 账簿服务-bjzr.onrender.com(免费实例——首次请求冷启动约五十秒)。