Skip to content

Commit 78d9412

Browse files
committed
Stabilize Dubbo write tail latency
1 parent 2a0f221 commit 78d9412

13 files changed

Lines changed: 274 additions & 36 deletions

File tree

README.md

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Shared sample contracts come from `com.reactor.sample:rest-sample-utility:0.2.0`
1212
come transitively from `com.reactor.sample:rust-sample-model:0.2.0`. The Dubbo interface package name is
1313
kept as `com.reactor.rust.dubbo.sample` so the provider and consumer keep the same service identity.
1414

15-
[Release notes for v0.3.1](docs/RELEASE_NOTES_v0.3.1.md)
15+
[Release notes for v0.3.2](docs/RELEASE_NOTES_v0.3.2.md)
1616

1717
## Contents
1818

@@ -107,7 +107,7 @@ java "-Dreactor.dubbo.registry-enabled=false" `
107107
"-Dsample.db.password=reactor" `
108108
"-Dsample.db.schema-init=true" `
109109
"-Dsample.db.warmup=true" `
110-
-jar target/rest-sample-dubbo-provider-0.1.1.jar
110+
-jar target/rest-sample-dubbo-provider-0.3.2.jar
111111
```
112112

113113
Keep this terminal open.
@@ -128,7 +128,7 @@ java "-Dserver.port=8080" `
128128
"-Dreactor.dubbo.native-connections-per-endpoint=2" `
129129
"-Dreactor.dubbo.native-async-workers=1" `
130130
"-Dreactor.dubbo.max-inflight=8" `
131-
-jar target/rest-sample-dubbo-consumer-0.1.1.jar
131+
-jar target/rest-sample-dubbo-consumer-0.3.2.jar
132132
```
133133

134134
### 3. Call The Endpoints
@@ -409,7 +409,7 @@ same behavior.
409409
| Recipe | Use when | Key settings | Trade-off |
410410
|--------|----------|--------------|-----------|
411411
| `micro-1x1` | Smallest Dubbo consumer pod, low or moderate traffic, DB-backed provider with a small pool. | <small><code>reactor.runtime.profile=micro-dubbo</code><br><code>reactor.dubbo.native-connections-per-endpoint=1</code><br><code>reactor.dubbo.native-async-workers=1</code><br><code>reactor.dubbo.native-async-queue-capacity=32</code><br><code>reactor.dubbo.max-inflight=16</code></small> | Lowest RSS. Spikes may get controlled `503`. |
412-
| `micro-2x2` | Full sample writes to DB, or Hikari is `2` and `1x1` serializes calls before the DB. | <small><code>sample.dubbo.capacity-profile=micro-2x2</code><br><code>reactor.dubbo.native-connections-per-endpoint=2</code><br><code>reactor.dubbo.native-async-workers=2</code><br><code>reactor.dubbo.native-async-queue-capacity=64</code><br><code>reactor.dubbo.max-inflight=64</code></small> | One extra native worker and connection. This is the full sample default; catalog-only stays `1x1`. |
412+
| `micro-2x2` | Full sample writes to DB, or Hikari is `2` and `1x1` serializes calls before the DB. | <small><code>sample.dubbo.capacity-profile=micro-2x2</code><br><code>reactor.dubbo.native-connections-per-endpoint=2</code><br><code>reactor.dubbo.native-async-workers=2</code><br><code>reactor.dubbo.native-async-queue-capacity=64</code><br><code>reactor.dubbo.max-inflight=64</code><br><code>POST /customers=4/250ms</code><br><code>POST /customers/typed=4/250ms</code></small> | Keeps the two hot create routes from building a 16-request queue in front of one shared `2x2` backend. This is the full sample default; catalog-only stays `1x1`. |
413413
| `balanced-stable-4x4` | Read-heavy service, provider returns ready JSON, and you want more 2xx without hiding overload. | <small><code>reactor.runtime.profile=balanced-dubbo</code><br><code>reactor.dubbo.native-connections-per-endpoint=4</code><br><code>reactor.dubbo.native-async-workers=4</code><br><code>reactor.dubbo.native-async-queue-capacity=512</code><br><code>reactor.dubbo.max-inflight=64</code></small> | Higher RPS. Needs provider CPU/DB proof. |
414414
| `balanced-mid-4x4` | Distributed commands need more accepted work and provider capacity is measured. | Same `4x4` native settings, with wider route budgets than `stable`. | More 2xx possible. p99/RSS must be watched. |
415415
| `balanced-wide-4x4` | Only for a measured high-capacity provider, not as a default. | Same `4x4` native settings, with the widest route budgets. | Can hide overload in queues. Use with caution. |
@@ -433,6 +433,11 @@ Use this rule for DB-backed endpoints: do not size the consumer for client concu
433433
from provider and Hikari capacity first. If the provider has only two DB connections, `c64` traffic
434434
must either wait briefly or get controlled `503`; deep queues only increase RSS and p99.
435435

436+
Route limits are not isolated capacity. If raw and typed create routes both call the same command
437+
service, their accepted work is added together. With a two-worker/two-connection backend, `8 + 8`
438+
allows sixteen requests to wait behind two durable PostgreSQL commits. The `micro-2x2` recipe uses
439+
`4 + 4` and a `250 ms` wait budget so WAL/fsync variance does not amplify across a deep queue.
440+
436441
Run the repeatable mixed raw/typed write gate after the consumer, provider, and PostgreSQL containers
437442
join the same Docker network:
438443

@@ -447,6 +452,17 @@ docker run --rm --network YOUR_NETWORK `
447452
The normal gate requires zero failed writes and p99 below `250 ms`. A high-concurrency overload test
448453
may allow controlled `503`, but it must not report native Dubbo errors, DB rollbacks, or pool exhaustion.
449454

455+
Use the repeat gate for the `c16` tail-latency check. It rejects a result when one run exceeds
456+
`200 ms` p99, the p99 spread exceeds `75 ms`, or non-2xx exceeds `0.10%`:
457+
458+
```powershell
459+
.\benchmark\dubbo-write-repeat-gate.ps1 `
460+
-Network YOUR_NETWORK `
461+
-BaseUrl http://consumer:8080 `
462+
-VirtualUsers 16 `
463+
-Repeats 3
464+
```
465+
450466
## Production Recipes
451467

452468
### Recipe 1: Kubernetes Consumer With ZooKeeper Discovery
@@ -496,7 +512,7 @@ Use this when the provider address is known and your REST API only exposes read
496512
mvn -q -Pnative-static-consumer package
497513
java -Xms8m -Xmx48m -Xss256k -XX:ActiveProcessorCount=1 `
498514
-Dreactor.dubbo.providers=provider:20880 `
499-
-jar target/rest-sample-dubbo-consumer-0.1.1.jar
515+
-jar target/rest-sample-dubbo-consumer-0.3.2.jar
500516
```
501517

502518
Effect:
@@ -512,8 +528,10 @@ Effect:
512528
Use this for POST/PATCH/DELETE routes where each REST request becomes one provider command call.
513529

514530
```properties
515-
reactor.rust.route-admission.post.api.v1.customers.max-concurrent=8
516-
reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms=150
531+
reactor.rust.route-admission.post.api.v1.customers.max-concurrent=4
532+
reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms=250
533+
reactor.rust.route-admission.post.api.v1.customers.typed.max-concurrent=4
534+
reactor.rust.route-admission.post.api.v1.customers.typed.queue-timeout-ms=250
517535
reactor.rust.route-admission.patch.api.v1.customers.id.segment.max-concurrent=8
518536
reactor.rust.route-admission.delete.api.v1.customers.id.max-concurrent=8
519537
reactor.dubbo.timeout-ms=1000
@@ -1999,9 +2017,9 @@ Route admission:
19992017
| `reactor.rust.route-admission.get.api.v1.customers.db.queue-timeout-ms` | `150` | Customer DB read queue wait. Lower for faster fail-fast under DB saturation. |
20002018
| `reactor.rust.route-admission.get.api.v1.customers.db.stats/by-segment/id.*` | `4-8` / `150` | Typed DB stats, list, and record lookup caps. Keep list queries lower than raw byte pass-through routes. |
20012019
| `reactor.rust.route-admission.get.api.v1.customers.id.exists/display-name.*` | `8` / `150` | Small scalar lookup caps. Tune with provider DB pool if these call the database. |
2002-
| `reactor.rust.route-admission.post.api.v1.customers.max-concurrent` | `8` | Create command cap. Keep bounded to avoid duplicate-write pressure and DB queue buildup. |
2003-
| `reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms` | `150` | Create command wait budget. Lower if write p99 matters more than absorbing bursts. |
2004-
| `reactor.rust.route-admission.post.api.v1.customers.typed.*` | `8` / `250` | Typed create cap for `micro-2x2`. This removes false admission timeouts at the normal write gate. |
2020+
| `reactor.rust.route-admission.post.api.v1.customers.max-concurrent` | `4` | Raw create cap for `micro-2x2`. Raw and typed limits add up because both use the same two RPC/DB permits. |
2021+
| `reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms` | `250` | Raw create wait budget. It absorbs short WAL/fsync variance without allowing a deep queue. |
2022+
| `reactor.rust.route-admission.post.api.v1.customers.typed.*` | `4` / `250` | Typed create cap for `micro-2x2`. Together with the raw route, at most eight create requests enter the shared backend path. |
20052023
| `reactor.rust.route-admission.patch.api.v1.customers.id.segment.max-concurrent` | `8` | Segment patch cap. Keep aligned with command provider capacity. |
20062024
| `reactor.rust.route-admission.patch.api.v1.customers.id.segment.queue-timeout-ms` | `150` | Segment patch queue wait. Raise only with idempotent caller behavior and measured p99. |
20072025
| `reactor.rust.route-admission.patch.api.v1.customers.id.status.max-concurrent` | `8` | Status patch cap. Keep bounded for write-side stability. |

README.tr.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ record'ları transitif olarak `com.reactor.sample:rust-sample-model:0.2.0` paket
1515
interface package adı `com.reactor.rust.dubbo.sample` olarak korunur. Böylece provider ve consumer
1616
aynı service kimliğini kullanmaya devam eder.
1717

18-
[v0.3.1 sürüm notları](docs/RELEASE_NOTES_v0.3.1.md)
18+
[v0.3.2 sürüm notları](docs/RELEASE_NOTES_v0.3.2.tr.md)
1919

2020
## İçindekiler
2121

@@ -110,7 +110,7 @@ java "-Dreactor.dubbo.registry-enabled=false" `
110110
"-Dsample.db.password=reactor" `
111111
"-Dsample.db.schema-init=true" `
112112
"-Dsample.db.warmup=true" `
113-
-jar target/rest-sample-dubbo-provider-0.1.1.jar
113+
-jar target/rest-sample-dubbo-provider-0.3.2.jar
114114
```
115115

116116
Bu terminal açık kalmalıdır.
@@ -131,7 +131,7 @@ java "-Dserver.port=8080" `
131131
"-Dreactor.dubbo.native-connections-per-endpoint=2" `
132132
"-Dreactor.dubbo.native-async-workers=1" `
133133
"-Dreactor.dubbo.max-inflight=8" `
134-
-jar target/rest-sample-dubbo-consumer-0.1.1.jar
134+
-jar target/rest-sample-dubbo-consumer-0.3.2.jar
135135
```
136136

137137
### 3. Endpoint'leri Deneyin
@@ -424,7 +424,7 @@ Benchmark raporu sadece raw RPS vermez; bilinçli olarak şu alanları da taşı
424424
| Reçete | Ne zaman kullanılır? | Ana ayarlar | Bedel |
425425
|--------|----------------------|-------------|-------|
426426
| `micro-1x1` | En küçük Dubbo consumer pod, düşük/orta trafik, küçük DB pool kullanan provider. | <small><code>reactor.runtime.profile=micro-dubbo</code><br><code>reactor.dubbo.native-connections-per-endpoint=1</code><br><code>reactor.dubbo.native-async-workers=1</code><br><code>reactor.dubbo.native-async-queue-capacity=32</code><br><code>reactor.dubbo.max-inflight=16</code></small> | En düşük RSS. Spike altında kontrollü `503` dönebilir. |
427-
| `micro-2x2` | Full sample DB'ye yazıyor veya Hikari `2` olduğu halde `1x1` çağrıları DB'ye ulaşmadan seri hale getiriyor. | <small><code>sample.dubbo.capacity-profile=micro-2x2</code><br><code>reactor.dubbo.native-connections-per-endpoint=2</code><br><code>reactor.dubbo.native-async-workers=2</code><br><code>reactor.dubbo.native-async-queue-capacity=64</code><br><code>reactor.dubbo.max-inflight=64</code></small> | Bir ek native worker ve connection kullanır. Full sample varsayılanıdır; catalog-only `1x1` kalır. |
427+
| `micro-2x2` | Full sample DB'ye yazıyor veya Hikari `2` olduğu halde `1x1` çağrıları DB'ye ulaşmadan seri hale getiriyor. | <small><code>sample.dubbo.capacity-profile=micro-2x2</code><br><code>reactor.dubbo.native-connections-per-endpoint=2</code><br><code>reactor.dubbo.native-async-workers=2</code><br><code>reactor.dubbo.native-async-queue-capacity=64</code><br><code>reactor.dubbo.max-inflight=64</code><br><code>POST /customers=4/250ms</code><br><code>POST /customers/typed=4/250ms</code></small> | İki yoğun create route'unun ortak `2x2` backend önünde 16 istek biriktirmesini engeller. Full sample varsayılanıdır; catalog-only `1x1` kalır. |
428428
| `balanced-stable-4x4` | Read-heavy servis, provider hazır JSON dönüyor ve overload saklanmadan daha çok 2xx isteniyor. | <small><code>reactor.runtime.profile=balanced-dubbo</code><br><code>reactor.dubbo.native-connections-per-endpoint=4</code><br><code>reactor.dubbo.native-async-workers=4</code><br><code>reactor.dubbo.native-async-queue-capacity=512</code><br><code>reactor.dubbo.max-inflight=64</code></small> | RPS artar. Provider CPU/DB ölçümü gerekir. |
429429
| `balanced-mid-4x4` | Distributed command endpoint'leri daha çok kabul edilen iş istiyor ve provider kapasitesi ölçüldü. | Aynı `4x4` native ayarları, `stable` değerinden daha geniş route budget. | Daha çok 2xx alınabilir. p99/RSS izlenmelidir. |
430430
| `balanced-wide-4x4` | Sadece ölçülmüş yüksek kapasiteli provider için. Default yapılmamalıdır. | Aynı `4x4` native ayarları, en geniş route budget. | Overload queue içinde saklanabilir. Dikkatli kullanın. |
@@ -448,6 +448,12 @@ DB-backed endpoint için kural basittir: consumer'ı önce client concurrency de
448448
provider ve Hikari kapasitesine göre boyutlandırın. Provider'da iki DB connection varsa `c64`
449449
trafik ya kısa süre beklemeli ya da kontrollü `503` almalıdır. Derin queue sadece RSS ve p99 artırır.
450450

451+
Route limitleri birbirinden bağımsız backend kapasitesi değildir. Raw ve typed create route'ları aynı
452+
command service'e gidiyorsa kabul ettikleri işler toplanır. İki worker ve iki DB connection önünde
453+
`8 + 8` kullanmak, iki güvenli PostgreSQL commit işleminin arkasında 16 istek biriktirir. `micro-2x2`
454+
reçetesi bu nedenle `4 + 4` ve `250 ms` bekleme bütçesi kullanır. WAL/fsync gecikmesi derin queue
455+
boyunca büyüyerek p99 değerini bozmaz.
456+
451457
Consumer, provider ve PostgreSQL container'ları aynı Docker network'üne bağlandıktan sonra tekrarlanabilir
452458
raw/typed karma write gate'i çalıştırın:
453459

@@ -462,6 +468,17 @@ docker run --rm --network YOUR_NETWORK `
462468
Normal gate sıfır hatalı write ve `250 ms` altında p99 ister. Yüksek concurrency overload testinde
463469
kontrollü `503` kabul edilebilir. Ancak native Dubbo hatası, DB rollback veya pool exhaustion olmamalıdır.
464470

471+
`c16` tail-latency kontrolü için repeat gate'i kullanın. Tek bir koşuda p99 `200 ms` değerini aşarsa,
472+
p99 yayılımı `75 ms` değerinden büyükse veya non-2xx oranı `%0,10` üzerine çıkarsa test başarısız olur:
473+
474+
```powershell
475+
.\benchmark\dubbo-write-repeat-gate.ps1 `
476+
-Network YOUR_NETWORK `
477+
-BaseUrl http://consumer:8080 `
478+
-VirtualUsers 16 `
479+
-Repeats 3
480+
```
481+
465482
## Production Reçeteleri
466483

467484
### Reçete 1: ZooKeeper Discovery Kullanan Kubernetes Consumer
@@ -511,7 +528,7 @@ expose ediyorsa bunu kullanın.
511528
mvn -q -Pnative-static-consumer package
512529
java -Xms8m -Xmx48m -Xss256k -XX:ActiveProcessorCount=1 `
513530
-Dreactor.dubbo.providers=provider:20880 `
514-
-jar target/rest-sample-dubbo-consumer-0.1.1.jar
531+
-jar target/rest-sample-dubbo-consumer-0.3.2.jar
515532
```
516533

517534
Etkisi:
@@ -528,8 +545,10 @@ Her REST isteğinin bir provider command çağrısına dönüştüğü POST/PATC
528545
kullanın.
529546

530547
```properties
531-
reactor.rust.route-admission.post.api.v1.customers.max-concurrent=8
532-
reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms=150
548+
reactor.rust.route-admission.post.api.v1.customers.max-concurrent=4
549+
reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms=250
550+
reactor.rust.route-admission.post.api.v1.customers.typed.max-concurrent=4
551+
reactor.rust.route-admission.post.api.v1.customers.typed.queue-timeout-ms=250
533552
reactor.rust.route-admission.patch.api.v1.customers.id.segment.max-concurrent=8
534553
reactor.rust.route-admission.delete.api.v1.customers.id.max-concurrent=8
535554
reactor.dubbo.timeout-ms=1000
@@ -2012,9 +2031,9 @@ Route admission:
20122031
| `reactor.rust.route-admission.get.api.v1.customers.db.queue-timeout-ms` | `150` | Customer DB read queue wait. DB saturation altında hızlı fail-fast için düşürün. |
20132032
| `reactor.rust.route-admission.get.api.v1.customers.db.stats/by-segment/id.*` | `4-8` / `150` | Typed DB stats, list ve record lookup cap'leri. List query'leri raw byte pass-through route'larından düşük tutun. |
20142033
| `reactor.rust.route-admission.get.api.v1.customers.id.exists/display-name.*` | `8` / `150` | Küçük scalar lookup cap'leri. Bu method'lar DB'ye gidiyorsa provider DB pool ile tune edin. |
2015-
| `reactor.rust.route-admission.post.api.v1.customers.max-concurrent` | `8` | Create command cap. Duplicate write baskısı ve DB queue büyümesini önlemek için bounded kalmalı. |
2016-
| `reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms` | `150` | Create command wait budget. Write p99 burst absorption'dan önemliyse düşürün. |
2017-
| `reactor.rust.route-admission.post.api.v1.customers.typed.*` | `8` / `250` | `micro-2x2` typed create sınırıdır. Normal write gate'teki hatalı admission timeout'larını kaldırır. |
2034+
| `reactor.rust.route-admission.post.api.v1.customers.max-concurrent` | `4` | `micro-2x2` raw create sınırıdır. Raw ve typed limitleri aynı iki RPC/DB iznini kullandıkları için toplanır. |
2035+
| `reactor.rust.route-admission.post.api.v1.customers.queue-timeout-ms` | `250` | Raw create bekleme bütçesidir. Derin queue oluşturmadan kısa WAL/fsync dalgalanmasını karşılar. |
2036+
| `reactor.rust.route-admission.post.api.v1.customers.typed.*` | `4` / `250` | Typed create sınırıdır. Raw route ile birlikte ortak backend yoluna en fazla sekiz create isteği girer. |
20182037
| `reactor.rust.route-admission.patch.api.v1.customers.id.segment.max-concurrent` | `8` | Segment patch cap. Command provider kapasitesiyle hizalayın. |
20192038
| `reactor.rust.route-admission.patch.api.v1.customers.id.segment.queue-timeout-ms` | `150` | Segment patch queue wait. Sadece idempotent caller ve p99 ölçümü varsa artırın. |
20202039
| `reactor.rust.route-admission.patch.api.v1.customers.id.status.max-concurrent` | `8` | Status patch cap. Write-side stabilite için bounded kalmalı. |

0 commit comments

Comments
 (0)