سرویس gRPC ممکن است در تست «یک پاسخ درست» بدهد و در Production باز هم دو بار پرداخت ثبت کند، یک stream را پس از قطع کلاینت باز نگه دارد یا با تغییر ظاهراً بیخطر شمارهٔ field، دادهٔ قدیمی را اشتباه بخواند. تفاوت از اینجا میآید که تست gRPC فقط اعتبارسنجی payload نیست؛ قرارداد Protobuf، چرخهٔ RPC، deadline، cancellation، status، metadata، retry، HTTP/۲، streaming و رفتار کلاینتهای قدیمی را هم دربرمیگیرد.
این راهنما یک مسیر عملی برای QA و توسعهدهنده ارائه میکند: ابتدا قرارداد و ریسک را تعریف میکنیم، چهار نوع RPC را جدا میآزماییم، سپس ابزارهایی مانند grpcurl، Buf و ghz را در جای درست قرار میدهیم. مثال اصلی، سرویس استعلام و تسویهٔ پرداخت ایرانی است تا مسئلههای idempotency، ریال/تومان، شناسهٔ PSP و شبکهٔ ناپایدار ملموس باشند.
اصل کلیدی: «فایل proto کامپایل شد» فقط سازگاری نحوی را نشان میدهد. «پیام قدیمی decode شد» فقط بخشی از wire compatibility است. قرارداد واقعی شامل معنای field، status code، metadata، deadline، ترتیب و مالکیت retry نیز هست.
gRPC چه چیز متفاوتی برای تست دارد؟
gRPC سرویس و messageها را معمولاً با Protocol Buffers تعریف و کد client/server را تولید میکند. بر اساس مفاهیم اصلی gRPC چهار شکل RPC داریم:
| نوع RPC | جریان | ریسکهای محوری تست |
|---|---|---|
| Unary | یک request، یک response | validation، status، deadline، retry و idempotency |
| Server streaming | یک request، چند response | ترتیب، پایان stream، slow consumer، cancellation و partial result |
| Client streaming | چند request، یک response | half-close، duplicate، backpressure، aggregation و قطع میانی |
| Bidirectional streaming | دو stream مستقل | همزمانی، state machine، race، flow control و cleanup دوطرفه |
gRPC ترتیب messageها را درون یک RPC منفرد حفظ میکند، اما این به معنی exactly-once بودن عملیات کسبوکار، ترتیب میان چند stream یا نبود event تکراری در source نیست. همین مرز باید در Test Oracle نوشته شود.
برای اصول مشترک status/body/schema/auth و طراحی مجموعهٔ API، مبانی تست API را در کنار این راهنمای اختصاصی بخوانید.
قبل از ابزار، «قرارداد قابل مشاهده» بسازید
برای هر method یک Contract Card نگه دارید:
- نام کامل package/service/method و نسخهٔ proto؛
- نوع RPC و state machine جریان؛
- معنای هر field، واحد، timezone، دامنه و حضور/عدم حضور؛
- قواعد validation و status code مورد انتظار؛
- initial/trailing metadata و سیاست ثبت آن؛
- deadline budget و propagation به dependencyها؛
- سیاست retry/hedging و شرط idempotency؛
- احراز هویت، مجوز، TLS/mTLS و مرز tenant؛
- حد پیام، compression و resource budget؛
- وابستگیها، side effectها و trace/audit evidence؛
- نسخههای client قدیمی که باید همزمان پشتیبانی شوند.
این کارت، proto را به رفتار وصل میکند. اگر برای amount مشخص نباشد واحد ریال است یا تومان، wire کاملاً معتبر نیز میتواند تراکنش دهبرابری بسازد.
تست قرارداد Protobuf و تکامل سازگار
نمونهٔ قرارداد
syntax = "proto3";
package payment.v1;
service PaymentInquiryService {
rpc GetPayment(GetPaymentRequest) returns (GetPaymentResponse);
rpc WatchSettlement(WatchSettlementRequest)
returns (stream SettlementEvent);
}
message GetPaymentRequest {
string request_id = 1;
string authority = 2;
int64 amount_rial = 3;
}
message GetPaymentResponse {
string payment_id = 1;
PaymentState state = 2;
int64 amount_rial = 3;
reserved 4;
reserved "legacy_status";
}
enum PaymentState {
PAYMENT_STATE_UNSPECIFIED = 0;
PAYMENT_STATE_PENDING = 1;
PAYMENT_STATE_PAID = 2;
PAYMENT_STATE_FAILED = 3;
}
این نمونه آموزشی است؛ schema واقعی باید policy نسخهبندی و امنیت سازمان را دنبال کند. request_id بهتنهایی idempotency را ایجاد نمیکند—سرور باید uniqueness، scope، retention و پاسخ replay را تعریف کند.
تغییرهای امن و خطرناک
راهنمای رسمی Proto3 تأکید میکند شمارهٔ field پس از استفاده نباید تغییر یا دوباره استفاده شود و شماره/نام حذفشده بهتر است reserved بماند. ماتریس زیر را در code review اجرا کنید:
| تغییر | بررسی مکانیکی | آزمون معنایی |
|---|---|---|
| افزودن field | tag یکتا و policy حضور | کلاینت قدیمی نبود field را چگونه میفهمد؟ سرور قدیمی unknown field را چه میکند؟ |
| حذف field | reserve شماره و در صورت نیاز نام | آیا client فعال یا دادهٔ ذخیرهشده هنوز به معنا وابسته است؟ |
| تغییر type | wire/JSON/source compatibility | range، sign، precision و رفتار همهٔ زبانها |
| rename | اثر بر generated code و JSON/TextFormat | dashboards، mapping و مصرفکنندههای غیر binary |
| افزودن enum | مقدار صفر و unknown enum handling | کلاینت قدیمی state جدید را Fail-safe میبیند یا Paid فرض میکند؟ |
| تغییر معنا/واحد | ممکن است هیچ checkerی خطا ندهد | Golden payload و dual-version integration الزامی است |
| حذف method/service | breaking-change check | inventory کلاینت و rollout/migration |
Buf Breaking Change Detection میتواند نسخهٔ فعلی schema را با مرجع Git/registry/image مقایسه کند. policyهای FILE، PACKAGE، WIRE_JSON و WIRE پوشش متفاوت دارند. عبور از checker ثابت نمیکند تغییر معنایی امن است؛ custom option و رفتار application نیز ممکن است بیرون دامنهٔ rule باشد.
ماتریس دو نسخه
حداقل چهار ترکیب را اجرا کنید: client قدیم ↔ server قدیم، client جدید ↔ server قدیم، client قدیم ↔ server جدید، client جدید ↔ server جدید. Golden binary payload را serialize/deserialize کنید و fieldهای unknown، default/absence، enum جدید و دادهٔ persisted را بررسی کنید. قرارداد cross-language را با حداقل زبانهای واقعی مصرفکننده اجرا کنید؛ یک تست Java تضمین رفتار Node.js یا Go نیست.
برای جایگاه این لایه در تعامل componentها، راهنمای تست یکپارچهسازی و برای الگوهای consumer/provider، مقدمهٔ تست قرارداد API مفیدند؛ اما Pact را بهصورت خودکار جایگزین compatibility check مخصوص Protobuf ندانید.
هرم تست gRPC
Unit: منطق را از transport جدا کنید
validation، authorization decision، mapping و domain logic را بدون شبکه سریع تست کنید. context، clock، repository و downstream client را با double کنترل کنید. روی جزئیات generated stub بیش از حد mock نسازید؛ چنین تستی ممکن است implementation فرضی خودتان را تأیید کند، نه رفتار gRPC.
In-process: سریع اما محدود
سرور/کانال in-process برای interceptor، serialization و handler مناسب است، ولی بسته به زبان ممکن است HTTP/۲ socket، TLS، proxy، DNS، compression، message limit و flow control واقعی را دور بزند. نتیجه را «integration کامل» ننامید.
Transport integration: server واقعی روی پورت موقت
server واقعی را با port تصادفی، credential آزمایشی و dependencyهای کنترلشده اجرا کنید. generated client واقعی، TLS، metadata، deadline، cancellation و status/trailer را مشاهده کنید. تست deterministic باید readiness را poll کند؛ sleep ثابت منبع flaky test است.
Component و End-to-End
در component test، دیتابیس/queue واقعی و مرزهای بیرونی virtualized میشوند. E2E فقط سفرهای پرریسک را از entry point تا side effect نهایی میپوشاند. توزیع effort را با context تنظیم کنید؛ هرم تست در عمل برای جلوگیری از E2E-heavy suite راهنماست.
Unary RPC: فراتر از happy path
برای هر request، partitionهای زیر را بسازید:
- field حاضر، غایب، empty/default و بیشینهٔ مجاز؛
- Unicode و normalization، ارقام فارسی/عربی/لاتین و متن RTL؛
- unknown enum یا field از client جدید؛
- message زیر، دقیقاً روی و بالای size limit؛
- duplicate request_id، همان کلید با payload یکسان و payload متناقض؛
- dependency success/timeout/unavailable/invalid response؛
- client cancellation پیش و پس از شروع side effect؛
- deadline قبل از dispatch، هنگام dependency و بعد از commit؛
- نقش و tenant درست، اشتباه، منقضی و بدون credential؛
- تکرار همزمان برای race روی uniqueness یا balance.
response موفق را فقط با equality چند field نسنجید. side effect در ledger، event خروجی، audit، trace و عدم duplicate را هم بررسی کنید. در failure نیز absence اثر ناخواسته یک assertion مهم است.
Streaming: state machine را تست کنید
Server streaming
صفر، یک و چند message؛ ترتیب؛ پایان طبیعی؛ status نهایی؛ قطع server؛ reconnect محصول؛ slow consumer؛ cancel پس از N پیام و resource cleanup را اجرا کنید. message ordering در همان RPC را با sequence number قابل مشاهده بسنجید. دریافت اولین پیام success کل stream را ثابت نمیکند؛ trailer و terminal status را بخوانید.
Client streaming
کلاینت باید پایان ارسال یا half-close را درست اعلام کند. stream خالی، batch کوچک/بزرگ، message نامعتبر در میانه، duplicate، pause طولانی، cancel پیش از half-close و disconnect پس از پذیرش بخشی را تست کنید. Oracle باید atomicity را روشن کند: همه یا هیچ، partial commit یا per-item result؟
Bidirectional streaming
send و receive الزاماً lockstep نیستند. مدل state بنویسید و transitionهای مجاز را تست کنید: connect، authenticate، subscribe، send، ack، error، reconnect و close. race میان cancel و response، چند writer ناخواسته، پیام پس از terminal status، duplicate پس از reconnect و cleanup goroutine/thread را هدف بگیرید.
Backpressure و flow control
sender سریع و receiver کند بسازید، buffer و حافظه را پایش و ببینید write چه زمانی block/await میشود. در gRPC، پذیرفته شدن write توسط framework لزوماً به معنی خروج message روی شبکه نیست. سناریوی آزمایش باید bounded memory، fairness، cancellation responsiveness و عدم deadlock را اثبات کند.
Deadline، cancellation و propagation
مستند رسمی gRPC Deadlines میگوید بهطور پیشفرض deadline تعیین نمیشود و client ممکن است عملاً بینهایت منتظر بماند. client باید budget واقعبینانهای بر اساس SLO و load test تعیین کند.
این سناریوها را اجرا کنید:
- پاسخ کمی قبل و کمی بعد از deadline؛
- deadline بسیار کوتاه و نرفتن کار پرهزینه؛
- propagation از Service A به B و کسر زمان مصرفشده؛
- خاتمهٔ query/job/stream پس از cancellation؛
- عدم commit یا تعریف reconciliation اگر commit همزمان با timeout رخ داد؛
- تفکیک
DEADLINE_EXCEEDEDسمت client از status یا log سمت server؛ - proxy/service mesh با timeout کوتاهتر یا بلندتر از application؛
- resource/metric leak پس از هزاران cancellation.
client ممکن است deadline ببیند، در حالی که server عملیات را commit کرده است. برای عملیات مالی، query-by-idempotency-key یا status lookup باید ambiguity را حل کند؛ retry کور خطرناک است.
Status code، error detail و metadata
Status code را بخشی از API بدانید
راهنمای Status Codes در gRPC میان خطای مستقل از state مانند INVALID_ARGUMENT، نیازمند اصلاح state مانند FAILED_PRECONDITION، تعارض concurrency مانند ABORTED و خطای گذرای قابل retry مانند UNAVAILABLE فرق میگذارد. برای هر rule، code و retry semantics را در Contract Card بنویسید.
| وضعیت نمونه | سناریوی تست | ضدالگو |
|---|---|---|
| INVALID_ARGUMENT | amount منفی یا authority با قالب نامعتبر | برگرداندن UNKNOWN برای هر validation |
| UNAUTHENTICATED | credential غایب/نامعتبر | یکی گرفتن با PERMISSION_DENIED |
| PERMISSION_DENIED | هویت معتبر بدون دسترسی merchant | افشای وجود resource از tenant دیگر |
| NOT_FOUND | resource در scope مجاز وجود ندارد | تفاوت پیام که enumeration میسازد |
| ALREADY_EXISTS | ایجاد دوبارهٔ شناسهٔ یکتا | تعارض با replay موفق idempotent |
| ABORTED | تعارض optimistic concurrency | retry همان call وقتی کل transaction باید از نو خوانده شود |
| UNAVAILABLE | dependency یا instance گذرا unavailable | retry عملیات غیر idempotent بدون کلید |
| INTERNAL/DATA_LOSS | شکست invariant یا فساد جدی | افشای stack، SQL یا token در message |
Metadata و trailer
Metadata میتواند credential، trace ID، locale، idempotency key و اطلاعات سفارشی حمل کند؛ trailer نیز نتیجهٔ نهایی و detail را. presence/absence، duplicate key، binary value، case، limit، propagation و redaction را تست کنید. token را در artifact یا command history نگه ندارید. error message باید برای client قابل عمل باشد اما PII و جزئیات داخلی نشت ندهد.
Retry، idempotency و retry storm
طبق راهنمای رسمی Retry، زیرساخت retry فعال است اما بدون retry policy، بیشتر سناریوها به retry شفاف محدود میشوند. policy صریح میتواند maxAttempts، backoff، retryable codes و throttling را تعیین کند.
ماتریس تست باید شامل این موارد باشد:
- failure پیش از رسیدن به application و پس از شروع handler؛
- response header دریافتشده و commit شدن RPC برای retry engine؛
- attempt count، backoff/jitter و deadline کل؛
- کلید idempotency یکسان با payload یکسان، متناقض و منقضی؛
- server pushback یا throttling در صورت استفاده؛
- retry چند لایه در app، library، proxy و service mesh؛
- partial stream و اینکه resume، restart یا failure نهایی است؛
- افزایش بار در outage و جلوگیری از retry storm.
بودجهٔ retry را در یک لایه مالک کنید. سه attempt در سه لایه میتواند به چند برابر درخواست تبدیل شود. metric جدا برای logical call و physical attempt نگه دارید.
Health checking و reflection
Health با readiness یکی نیست مگر تعریف کنید
gRPC سرویس استاندارد Health Checking با grpc.health.v1، متد unary Check و stream Watch دارد. owner باید status را بهروز کند. تست کنید:
- کل server و service خاص چه زمانی SERVING/NOT_SERVING میشوند؛
- dependency حیاتی و غیرحیاتی چه اثری دارند؛
- shutdown ابتدا NOT_SERVING را اعلام و سپس streamها را drain میکند؛
- Watch تغییر وضعیت و reconnect را درست میرساند؛
- load balancer instance ناسالم را کنار میگذارد و پس از recovery برمیگرداند؛
- نام service ناشناخته status مورد انتظار را میدهد.
Reflection یک capability اختیاری است
Server Reflection descriptor سرویس را در runtime برای ابزارها آشکار میکند، اما باید در server فعال شود و ممکن است در endpoint عمومی عمداً بسته باشد. دو lane داشته باشید: محیط مهندسی با reflection و Production-like با policy واقعی. وقتی reflection خاموش است، grpcurl میتواند از proto یا protoset استفاده کند.
امنیت transport و مجوز
امنیت را در سه لایه بسنجید:
- Channel: TLS، trust chain، hostname، expiry، protocol/cipher policy و mTLS در صورت نیاز؛
- Identity: credential معتبر، منقضی، audience/issuer اشتباه، revoke و rotation؛
- Authorization: method، resource، tenant، field و side effect.
فقط interceptor unit test کافی نیست؛ generated client واقعی را با channel امن اجرا کنید. plaintext را تنها در محیط محلی کنترلشده به کار ببرید. reflection، health و admin service نیز policy دسترسی میخواهند. metadata و error detail نباید credential، شمارهٔ کارت، کد ملی یا stack trace را وارد log کنند.
ابزارها: هرکدام برای یک سؤال
| ابزار | بهترین کاربرد | محدودیت |
|---|---|---|
| generated client + test framework | assertion دقیق، streaming، deadline/cancel و CI | نیازمند کد و نگهداری per-language |
| grpcurl | کاوش، smoke، metadata/status و اسکریپت ساده | جای suite معنایی و load model را نمیگیرد |
| Postman/Kreya | کاوش GUI، اشتراک request و آزمایش دستی | capability و automation/streaming را در نسخهٔ جاری تأیید کنید |
| Buf | lint، build و breaking-change gate برای schema | semantic change و رفتار runtime را کامل نمیسنجد |
| ghz | load/benchmark مخصوص gRPC | درستی workload، داده و زیرساخت همچنان با شماست |
| language test utilities | in-process server، fake clock و controlled interceptor | API و capability میان زبانها متفاوت است |
نمونهٔ grpcurl
مخزن رسمی grpcurl امکان استفاده از reflection، فایل proto یا protoset را مستند کرده است. نمونهٔ محلی:
grpcurl -plaintext \
-import-path ./proto \
-proto payment/v1/payment.proto \
-d '{"requestId":"qa-1001","authority":"A123","amountRial":"250000"}' \
localhost:50051 payment.v1.PaymentInquiryService/GetPayment
در ProtoJSON، نمایش برخی نوعها مانند int64 ممکن است string باشد؛ این را با قرارداد ابزار اشتباه نگیرید. برای محیط غیرمحلی از TLS/CA و مدیریت secret استفاده کنید و token واقعی را در خط فرمان یا گزارش CI چاپ نکنید.
Load با ghz
ghz ابزار load و benchmark برای gRPC است. پیش از اجرا workload contract بنویسید: نرخ ورود یا concurrency، channel/connection count، مدت و warm-up، payload mix، unary/stream، TLS، deadline، retry policy و دادهٔ یکتا. بدون این موارد، عدد RPS قابل بازتولید نیست.
تست عملکرد gRPC
محدودهٔ stopwatch را مشخص کنید: client-observed end-to-end یا handler/server time. latency را با p50/p90/p95/p99 و حداکثر معنادار، throughput را با goodput موفق، و خطاها را به تفکیک status گزارش کنید. همزمان CPU، حافظه، GC، thread/goroutine، queue، connection، active stream، flow-control stall، message bytes و dependency را ببینید.
این experimentها معمولاً ارزش دارند:
- payload کوچک/متوسط/بزرگ و compressible/incompressible؛
- channel reuse در برابر connection churn؛
- concurrency پلکانی تا نقطهٔ شکست و recovery؛
- stream کوتاه و طولانی با producer/consumer کند؛
- TLS و mTLS مطابق Production؛
- retry خاموش/مصوب هنگام درصد کنترلشدهٔ UNAVAILABLE؛
- rolling restart و instance drain؛
- soak برای memory/stream leak و connection stability.
client load generator خودش میتواند bottleneck باشد؛ CPU/network آن و توزیع generatorها را ثبت کنید. برای طراحی ظرفیت، برنامهریزی تست عملکرد را به این workload اختصاصی وصل کنید.
مثال ایرانی: استعلام پرداخت و stream تسویه
سناریو فرضی است. سرویس داخلی فروشگاه از GetPayment برای استعلام و از WatchSettlement برای دریافت eventهای تسویه استفاده میکند.
قرارداد کسبوکار
- واحد مبلغ همیشه ریال و type صحیح است؛ تومان فقط در لبهٔ UI تبدیل میشود.
authorityopaque است و با ارقام/حروف مجاز validate میشود.request_idدر scope فروشنده idempotency key است.- PAID فقط پس از تطبیق مبلغ، فروشنده و پاسخ معتبر PSP برگردد.
- deadline کلاینت ۸۰۰ms است و بخشی از budget به PSP adapter میرسد.
- UNAVAILABLE قابل retry است؛ نتیجهٔ مبهم پس از deadline با lookup حل میشود.
آزمونهای Unary
مبلغ ۲۵۰٬۰۰۰ ریال با ۲۵٬۰۰۰ اشتباه نمیشود؛ رشتهٔ مبلغ، اعشار و عدد منفی رد میشوند؛ ارقام فارسی در edge قبل از gRPC normalize یا طبق contract رد میشوند. دو درخواست همزمان با request_id یکسان فقط یک ledger effect میسازند. payload متناقض با همان کلید باید status مشخص و بدون overwrite بدهد.
Deadline و ambiguity
PSP adapter تراکنش را commit میکند اما پاسخ پس از deadline میرسد. client DEADLINE_EXCEEDED میبیند؛ retry با همان key نباید پرداخت دوم بسازد و status lookup باید نتیجهٔ قبلی را برگرداند. trace باید logical call و attemptها را با request_id وصل کند.
Streaming تسویه
پیامهای stream در همان RPC مرتباند، اما source ممکن است duplicate یا late event بدهد. consumer با event_id deduplicate و checkpoint میکند. قطع پس از event شمارهٔ ۵۰، reconnect از cursor، consumer کند، cancellation، snapshot/stream race و rollout server تست میشوند. هیچ event مالی نباید فقط به اتکای transport «exactly once» فرض شود.
شواهد انتشار
گزارش شامل نسخه proto/client/server، descriptor hash، service config، device/network zone، build/commit، payload synthetic، correlation/request ID، status/trailer، ledger assertion، stream cursor، metrics و trace است. دادهٔ واقعی کارت یا token PSP وارد artifact نمیشود.
CI/CD پیشنهادی
- هر PR: format/lint/compile proto، Buf breaking مقابل main، unit و in-process؛
- Component lane: server واقعی، generated clients چندزبان، TLS، status، deadline و dependency container؛
- شبانه: streaming state model، cancellation leak، compatibility client قدیم/جدید و fault injection؛
- پیش از انتشار: load، rolling restart، retry storm، health/load-balancer و policy امنیت؛
- پس از انتشار: canary، status distribution، p95/p99، active streams، retry attempts و error budget.
artifactهای proto/protoset و generated SDK را immutable و قابل ردیابی کنید. تغییر service config هم مانند تغییر کد review و test میخواهد. برای feedback و quality gate، راهنمای تست پیوسته را ببینید؛ محیط و certificateهای آزمایشی نیز در مدیریت محیط تست باید owner و expiry داشته باشند.
خطاهای رایج
- فقط کامپایل proto: wire، source و semantic compatibility سه چیز متفاوتاند.
- تغییر واحد با همان field: checker ممکن است عبور دهد اما کسبوکار میشکند.
- mock کردن generated stub در همهجا: transport، serialization و interceptor هرگز اجرا نمیشوند.
- in-process برابر Production: TLS، HTTP/۲، proxy و flow control ممکن است حذف شوند.
- یک deadline ثابت برای همه: budget از SLO و dependency chain میآید.
- retry هر UNAVAILABLE: idempotency و commit ambiguity را نادیده میگیرد.
- میانگین latency: tail، error و goodput پنهان میشوند.
- stream بدون terminal assertion: partial success با success کامل اشتباه میشود.
- health برابر process alive: readiness سرویس و dependency تعریف نشده است.
- reflection همیشه روشن: policy محیط و سطح افشای schema نادیده گرفته میشود.
- metadata در log: credential و PII وارد artifact میشوند.
- RPS بدون workload manifest: نتیجه قابل مقایسه و تصمیمگیری نیست.
چکلیست تست gRPC
- Contract Card برای method، field، status، metadata و deadline وجود دارد.
- schema در CI lint/compile و مقابل نسخهٔ مرجع breaking-check میشود.
- golden payload و ماتریس client/server قدیم و جدید اجرا میشوند.
- هر چهار نوع RPC applicable با state و terminal status پوشش دارند.
- deadline، propagation، cancellation و cleanup آزموده شدهاند.
- retry policy، idempotency، attempt count و storm guardrail روشناند.
- status code و error detail بدون نشت داده قراردادی هستند.
- TLS/mTLS، authn، authz و tenant isolation روی transport واقعی تست شدهاند.
- health، reflection و admin service مطابق policy محیطاند.
- load manifest شامل channel، concurrency، TLS، payload، stream و retry است.
- شواهد به proto/client/server/service-config نسخهدار متصلاند.
- CI، canary و production telemetry یک taxonomy status/metric مشترک دارند.
سؤالات متداول
برای شروع تست دستی gRPC چه ابزاری مناسب است؟
grpcurl برای list/describe/invoke و اسکریپت smoke انتخاب سادهای است؛ از reflection یا proto/protoset استفاده میکند. GUIهایی مثل Postman و Kreya برای کاوش تیمی مفیدند. برای regression مهم، generated client و assertionهای زبان محصول را نیز بسازید.
آیا عبور Buf breaking یعنی تغییر proto کاملاً امن است؟
خیر. Buf تغییرهای مکانیکی را طبق policy انتخابی پیدا میکند. تغییر معنی، واحد، default کسبوکار، custom option، deadline یا status ممکن است بدون تغییر wire رخ دهد. compatibility test دو نسخه و golden scenario هنوز لازم است.
چرا باید برای هر gRPC call deadline تعیین کنیم؟
چون gRPC بهطور پیشفرض deadline ندارد و انتظار میتواند بسیار طولانی شود. deadline منابع client/server را محدود میکند، اما باید از SLO و dependency budget بیاید و cancellation به کارهای پاییندست برسد.
آیا میتوان همهٔ UNAVAILABLEها را retry کرد؟
نه لزوماً. باید بدانید عملیات idempotent است، server شاید side effect را انجام داده یا نه، deadline چقدر مانده و چند لایه retry میکنند. برای عملیات مالی، idempotency key و lookup/reconciliation ضروری است.
چطور streaming RPC را تست کنیم؟
آن را state machine ببینید: پیام صفر/چندگانه، ترتیب درون RPC، slow peer، flow control، half-close، cancellation، terminal status، reconnect، duplicate و resource cleanup. موفقیت اولین پیام، موفقیت stream نیست.
جمعبندی
تست gRPC از ارسال JSON به یک method فراتر میرود. قرارداد Protobuf را مکانیکی و معنایی نسخهبندی کنید، client/serverهای قدیم و جدید را کنار هم اجرا کنید، status و metadata را بخشی از API بدانید و deadline/cancellation/retry را با side effect واقعی بسنجید. برای streamها state machine و backpressure بسازید و برای load، workload manifest و tail latency داشته باشید. ابزار خوب سرعت میدهد؛ اما Contract Card، شواهد نسخهدار و آزمایش failure هستند که سرویس را برای تغییر و اختلال قابل اعتماد میکنند.

