Distributed Objects & Remote Invocation
Request–Reply Protocol ในบทที่แล้วยังเป็นระดับ "ข้อความดิบ" — บทนี้ห่อหุ้มมันให้ดูเหมือนการเรียก function/method ธรรมดา (Remote Procedure Call / Remote Method Invocation) พร้อมตอบคำถามที่สำคัญที่สุดข้อหนึ่งของวิชา: เมื่อ remote call ล้มเหลว ระบบควร "รับประกัน" อะไรได้บ้าง
1แนวคิด: ทำให้ Remote Call ดูเหมือน Local Call
2โครงสร้างของ Remote Object Reference
เพื่อให้ client อ้างอิงถึง object ที่อยู่บนเครื่องอื่นได้ ต้องมี reference ที่ระบุตัวตนได้ไม่ซ้ำกันทั่วทั้งระบบ ประกอบด้วย:
| องค์ประกอบ | ความหมาย |
|---|---|
| เลขที่อยู่ของโฮสต์ (IP address) | เครื่องที่ object นั้นอาศัยอยู่ |
| หมายเลขพอร์ต | Process/service ที่ object ทำงานอยู่บนเครื่องนั้น |
| เวลาที่ object ถูกสร้าง | ป้องกันการอ้างอิงผิดตัวหาก object เดิมถูกทำลายและมี object ใหม่มาแทนที่ในตำแหน่งเดียวกัน |
| หมายเลข object | กำหนดโดย process เจ้าของ ใช้แยก object หลายตัวในเครื่องเดียวกัน |
| Interface ที่ object implement อยู่ | บอกว่า object นี้เรียก method อะไรได้บ้าง |
3สถาปัตยกรรมของ Middleware: Proxy, Skeleton, Communication Module
| องค์ประกอบ | บทบาท |
|---|---|
| Proxy (Client stub) | อยู่ฝั่ง client — ทำตัวเหมือน object จริง เมื่อถูกเรียก method จะ marshal argument แล้วส่งข้อความไปหา server |
| Skeleton (Server stub / Dispatcher) | อยู่ฝั่ง server — unmarshal argument ที่รับมา แล้วเรียก method จริงของ object ที่ implement อยู่ จากนั้น marshal ผลลัพธ์ส่งกลับ |
| Communication Module | จัดการ request-reply protocol ที่เรียนในบทที่ 2 — ทั้งฝั่ง client และ server ใช้ module เดียวกัน |
| Remote Reference Module | แปลงระหว่าง local reference กับ remote object reference — สร้าง/ค้นหา proxy ให้ตรงกับ remote object ที่ต้องการ |
4กรณีศึกษาทางประวัติศาสตร์: CORBA และ Java RMI
มาตรฐานสำหรับระบบ distributed object ข้ามภาษา — ใช้ IDL (Interface Definition Language) เป็นกลางที่ไม่ผูกกับภาษาใดภาษาหนึ่ง ORB (Object Request Broker) ทำหน้าที่ส่ง request ระหว่าง client/server ที่อาจเขียนคนละภาษากัน
เรียบง่ายกว่าเพราะจำกัดเฉพาะ Java-to-Java — ใช้ Java interface เป็น IDL ในตัวเอง ส่ง object จริงผ่าน Java Serialization ได้ (ไม่ใช่แค่ primitive type) แต่แลกกับการไม่ข้ามภาษาได้เหมือน CORBA
5Invocation Semantics: รับประกันอะไรได้บ้างเมื่อ Remote Call ล้มเหลว
นี่คือหัวใจสำคัญที่สุดของบทนี้ — เมื่อ client เรียก remote method แล้วไม่ได้รับคำตอบ (เพราะ timeout) ระบบสามารถเลือก "รับประกัน" ได้ 3 ระดับ ซึ่งแต่ละระดับแลกความน่าเชื่อถือกับความซับซ้อนต่างกัน
| Semantics | พฤติกรรมเมื่อไม่ได้รับ reply | ความเสี่ยง |
|---|---|---|
| Maybe (มาก็ได้ ไม่มาก็ได้) | ไม่ retry เลย — เรียกครั้งเดียวแล้วจบ | Request อาจไม่ถูกประมวลผลเลยก็ได้ ไม่มีการรับประกันใด ๆ — ใช้ได้เฉพาะกรณีที่ยอมรับความสูญเสียได้ เช่น ส่งค่า sensor ที่ไม่สำคัญมาก |
| At-least-once | Retry จนกว่าจะได้ reply — ถ้า request เดิมไปถึงและถูกประมวลผลไปแล้วจริง (แค่ reply หาย) การ retry จะทำให้ operation ถูกเรียกซ้ำ | Operation ที่ไม่ idempotent จะเกิดผลข้างเคียงซ้ำซ้อน (เช่น ตัดเงินสองครั้ง) |
| At-most-once | Retry ได้ แต่ระบบ dedupe ด้วย request ID เพื่อไม่ให้ operation ถูกประมวลผลซ้ำ แม้ retry หลายครั้ง | ต้องการเก็บ state ฝั่ง server (ประวัติ request ID ที่เคยตอบแล้ว) — ซับซ้อนกว่าแต่ปลอดภัยกว่า |
6ทันสมัย: จาก IDL สู่ gRPC และ Protocol Buffers
| แนวคิดเดิม (CORBA/RMI) | แนวคิดสมัยใหม่ (gRPC) |
|---|---|
| IDL กำหนด interface ข้ามภาษา | .proto file กำหนด service และ message ข้ามภาษา generate stub ให้อัตโนมัติ (คล้ายกันมากในเชิงหลักการ) |
| ORB จัดการการส่ง request | gRPC runtime ทำงานบน HTTP/2 รองรับ streaming ทั้งสองทิศทาง (bidirectional streaming) ซึ่ง CORBA/RMI ทำไม่ได้ในตัว |
| CDR / Java Serialization | Protocol Buffers — binary, กระชับ, เร็ว |
- อธิบายว่าทำไมการทำให้ remote call "ดูเหมือน" local call จึงเป็นทั้งข้อดีและข้อเสียในเวลาเดียวกัน
- อธิบายองค์ประกอบของ remote object reference และเหตุผลที่ต้องมี "เวลาที่ object ถูกสร้าง" รวมอยู่ด้วย
- เปรียบเทียบ maybe, at-least-once, และ at-most-once invocation semantics พร้อมยกตัวอย่าง operation ที่เหมาะกับแต่ละแบบ
- อธิบายว่า at-most-once semantics เกี่ยวข้องกับ Idempotency Key ในวิชา Network Computing อย่างไร
- เปรียบเทียบ gRPC กับ REST ว่าเหมาะกับสถานการณ์ใดต่างกันอย่างไร โดยอ้างอิงกับแนวคิด RPC/RMI ในบทนี้
7Local Call กับ Remote Call: หน้าตาคล้าย แต่กฎคนละชุด
แนวคิด RPC/RMI ประสบความสำเร็จเพราะทำให้โปรแกรมเมอร์เรียกความสามารถบนอีกเครื่องด้วย syntax คล้าย function หรือ method ปกติ Stub ช่วย serialize argument ส่ง request รอ response แล้วแปลงค่ากลับมา ความซับซ้อนจำนวนมากจึงหายไปจากโค้ดธุรกิจ
ปัญหาคือ syntax ที่คล้ายกันชวนให้เราเผลอเชื่อว่า semantics คล้ายกันด้วย Local call ใช้เวลาโดยทั่วไปสั้น มี memory/process เดียว และถ้า function throw exception ผู้เรียกรู้ว่าการควบคุมกลับมาแล้ว Remote call ข้าม scheduler, network, proxy, server queue และ dependency หลายจุด ผู้เรียกอาจไม่รู้ว่า request ไปถึงหรือ side effect เกิดแล้วหรือยัง
| มิติ | Local call | Remote call |
|---|---|---|
| Latency | มักระดับ ns–µs และเสถียรกว่า | µs–seconds มี tail และ network variation |
| Failure | process เดียวกันมักล้มร่วมกัน | ผู้เรียก ผู้รับ และทางเชื่อมล้มแยกกัน |
| Arguments | ส่ง reference/pointer ได้ | ต้อง marshal by value หรือใช้ remote reference |
| Cancellation | ควบคุม stack เดียวกันได้มากกว่า | ผู้เรียกเลิกรอแต่ผู้รับอาจทำงานต่อ |
| Version | caller/callee deploy พร้อมกันใน binary เดียว | หลาย version อยู่พร้อมกันระหว่าง rollout |
| Security | อยู่ใน trust boundary เดียวกันบ่อย | ข้าม identity, network และ authorization boundary |
Fallacies of Distributed Computing
ความเชื่อผิดที่พบซ้ำ ได้แก่ network เชื่อถือได้ latency เป็นศูนย์ bandwidth ไม่จำกัด network ปลอดภัย topology ไม่เปลี่ยน มีผู้ดูแลคนเดียว transport cost เป็นศูนย์ และ network homogeneous แนวคิด remote transparency ทำให้ความเชื่อเหล่านี้แทรกในโค้ดได้ง่าย เพราะ call site ไม่แสดงว่ากำลังข้ามเครือข่าย
วิธีแก้ไม่ใช่เลิกใช้ RPC แต่ทำให้ boundary มองเห็นใน design: API client ต้องมี deadline, error model, retry policy และ telemetry; review ต้องนับ remote call ใน critical path; interface ควร coarse-grained พอไม่เกิด chatty traffic และ business operation ต้องนิยาม idempotency ก่อนเปิด auto-retry
8Proxy และ Skeleton ไม่ได้เป็นเพียงโค้ดประกอบ
Client proxy/stub ทำหน้าที่เป็นตัวแทน remote object: รับ method call, ตรวจ type, marshal argument, เติม metadata และส่งผ่าน communication module ฝั่ง server skeleton รับ request, unmarshal, dispatch ไป implementation แล้ว marshal result กลับ แนวคิดนี้ยังอยู่ใน generated gRPC client/server และ SDK ของ cloud service
แต่ proxy คือจุดที่ policy จำนวนมากถูกแทรกเข้ามา เช่น load balancing, retry, authentication token, tracing, circuit breaker และ caching ถ้าซ่อน policy เหล่านี้จนผู้เรียกไม่รู้ พฤติกรรม operation อาจเปลี่ยนโดยไม่เห็นจาก source code เช่นคำสั่งเขียนหนึ่งครั้งถูก library retry สามครั้ง
| หน้าที่ | สิ่งที่ proxy ทำได้ | ความเสี่ยง |
|---|---|---|
| Serialization | แปลง type เป็น wire format | schema/version และข้อมูลใหญ่เกิน |
| Discovery | แปล service name ไป instance | cache address เก่าและ stale endpoint |
| Load balancing | เลือก instance ต่อ request/connection | session/state ผูก instance หรือ load ไม่เท่ากัน |
| Retry | ซ่อน transient failure | duplicate side effect และ retry storm |
| Security | แนบ credential/ตรวจ TLS | token leakage และ trust boundary ไม่ชัด |
| Observability | สร้าง span และ metric สม่ำเสมอ | เห็น proxy latency แต่ไม่เห็น business step ภายใน |
Static proxy, Dynamic proxy และ Sidecar
Static/generated proxy ให้ type safety และ performance ดี แต่ต้อง regenerate เมื่อ contract เปลี่ยน Dynamic proxy ยืดหยุ่นและทำ cross-cutting concern ได้โดยไม่เขียนซ้ำ แต่ error อาจปรากฏตอน runtime ส่วน sidecar/service mesh ย้าย network policy ออกจาก process ไป proxy ข้าง service ทำให้หลายภาษาใช้ policy เดียวกันได้ แต่เพิ่ม hop, config และ failure mode ใหม่
ไม่มีตำแหน่งใด “โปร่งใสฟรี” ถ้า retry อยู่ใน library ทีม application ต้องรู้ ถ้า retry อยู่ใน sidecar ทีม platform ต้องสื่อสาร semantics ถ้าทั้งสองชั้น retry ซ้อนกัน จำนวน request ขยายแบบคูณ การรวม policy จึงเป็นปัญหาสถาปัตยกรรม ไม่ใช่ config ย่อย
9Remote Object Reference, Identity และ Lifetime
Reference ของ object ในเครื่องใช้ address หรือ runtime handle ที่มีความหมายภายใน process แต่ remote object reference ต้องระบุว่าติดต่อที่ไหน object ใด interface อะไร และ incarnation ใด เพราะ server restart แล้วสร้าง object ID เดิมขึ้นมาอาจไม่ใช่วัตถุเดียวกับก่อน crash
| องค์ประกอบ | หน้าที่ | ปัญหาถ้าขาด |
|---|---|---|
| Endpoint/service location | พา request ไปหา runtime | ไม่รู้เส้นทางหรือผูกตำแหน่งถาวรเกินไป |
| Object/service identifier | เลือก instance เชิงตรรกะ | dispatch ผิด object |
| Interface/type | รู้ operation และ schema | caller เรียก method ที่ไม่รองรับ |
| Incarnation/epoch | แยก instance ก่อนและหลัง restart | message เก่ากลับมากระทบ state ใหม่ |
| Security capability | ผูกสิทธิ์กับ reference บางระบบ | เดา ID แล้วเข้าถึง object ได้ |
Location transparency มักแก้ด้วย name service หรือ registry ให้ reference เชิงตรรกะแปลเป็น endpoint ปัจจุบัน แต่ cache ทำให้การย้ายไม่เกิดพร้อมกัน client บางตัวอาจยังเรียกที่เก่า ต้องมี TTL, draining, redirect หรือ compatibility ช่วงเปลี่ยนผ่าน
Distributed Garbage Collection
ใน process เดียว runtime รู้ reference ทั้งหมดได้ค่อนข้างครบ แต่ remote reference อาจอยู่บน client ที่ล่มหรือ network partition การไม่เห็น heartbeat ไม่ได้พิสูจน์ว่า reference หาย จึงใช้ lease: client เช่าสิทธิ์ช่วงเวลาหนึ่งและต้อง renew ถ้าไม่ renew server จึง reclaim resource
Lease เปลี่ยนปัญหาความรู้ไม่ครบเป็นนโยบายเวลา แต่ต้องรับมือ clock skew, pause และ network delay ค่า lease สั้นคืน resource เร็วแต่เสี่ยงหมดอายุผิด ค่า lease ยาวปลอดภัยกว่าแต่ resource ค้างนาน อีกทางคือ explicit close/release ร่วมกับ lease เป็น safety net เพราะ finalizer อย่างเดียวไม่แน่นอน
10Invocation Semantics แบบลงลึก
Maybe, at-least-once และ at-most-once ไม่ได้บอกว่า operation “สำเร็จแน่” แต่บอกขอบเขตจำนวนครั้งที่ server อาจ execute ภายใต้ retry/deduplication Maybe อาจไม่ทำเลยหรือทำหนึ่งครั้ง At-least-once มุ่งให้ทำอย่างน้อยหนึ่งแต่ซ้ำได้ At-most-once ป้องกันซ้ำแต่ยังอาจไม่ทำเลย
Client Network Server |--- request #42 ----------->| execute + commit | X <--- reply -------| reply lost |--- retry #42 ------------->| dedupe and return cached reply
At-most-once ต้องให้ server จำ request ID และผลเดิมในช่วงเวลาที่ retry อาจกลับมา ถ้าจำใน memory แล้ว server restart guarantee หาย ถ้าเขียน dedupe record แยกจาก business commit อาจ crash ตรงกลาง จึงควรทำ atomic ร่วมกันหรือมี unique constraint ที่ business state
Idempotent ไม่ได้แปลว่าเรียกซ้ำไม่มีต้นทุน
Operation idempotent ให้ผล state สุดท้ายเหมือนเรียกครั้งเดียว เช่น PUT ค่าเดิม แต่การเรียกซ้ำอาจยังเขียน audit log ส่ง notification หรือเสียค่าใช้จ่าย Idempotency ต้องกำหนดขอบเขตว่าครอบ side effect ใด และ key มี scope/expiry อย่างไร
| Operation | ตามธรรมชาติ | ทำให้ retry ปลอดภัยขึ้นอย่างไร |
|---|---|---|
| GET resource | มัก idempotent แต่ state อาจเปลี่ยนระหว่างครั้ง | ระวัง hidden side effect และ cache semantics |
| PUT /orders/123 | กำหนดปลายทางเดิม | conditional update/version ป้องกันเขียนทับ |
| POST /payments | อาจสร้างใหม่ทุกครั้ง | idempotency key + atomic unique record |
| Increment counter | ไม่ idempotent | ใช้ operation ID dedupe หรือส่ง desired value/version |
Retry Policy ต้องจำแนก Error
Unavailable, connection reset หรือ rate limit บางกรณีอาจ retry ได้ แต่ invalid argument, permission denied และ invariant conflict ไม่ดีขึ้นเพราะรอ การ retry write ต้องรู้ว่า server อาจ execute แล้ว ส่วน read อาจ retry ง่ายกว่าแต่ต้องคำนึง consistency และ deadline
ควรมี attempt limit, exponential backoff, jitter และ retry budget ระดับระบบ ไม่ใช่ทุกชั้นลองเอง หาก Gateway retry A และ A retry B อย่างละสามครั้ง request เดียวอาจสร้าง call ไป B เก้าครั้งก่อนนับ client retry
11Deadline, Cancellation และ Work ที่ไม่มีใครรอ
Timeout มักเป็นระยะเวลาต่อ call ส่วน deadline คือเวลาสิ้นสุดของงานทั้งหมด Deadline ส่งต่อได้และลดลงตามเวลาที่ใช้ไป ทำให้ downstream รู้ว่าเหลือเวลาเท่าใด การเริ่ม query ราคาแพงเมื่อเหลือ 5 ms มักไม่มีประโยชน์
Cancellation signal บอกว่า caller ไม่ต้องการผลแล้ว แต่ callee อาจผ่าน irreversible side effect ไปแล้ว จึงต้องออกแบบ cancellation point ถ้างานเป็น computation อ่านอย่างเดียวอาจหยุดได้ทันที ถ้าเป็นโอนเงินต้องให้ transaction จบอย่างสอดคล้อง แล้วตอบสถานะให้ตรวจภายหลังหรือทำ compensation
Hedged Request
ระบบอ่านบางชนิดลด tail latency ด้วยการส่ง request สำรองไป replica อื่นเมื่อคำขอแรกช้ากว่าค่าหนึ่ง แล้วใช้คำตอบแรก วิธีนี้แลก latency กับ load ต้องใช้เฉพาะ operation อ่าน/idempotent ควบคุมอัตรา และยกเลิกสำเนาที่เหลือ มิฉะนั้นช่วง overload จะเพิ่มงานให้ระบบที่กำลังช้า
12Interface Design: จาก Method เล็ก ๆ ไปสู่ Business Capability
Distributed object แบบละเอียดชวนออกแบบ interface ตาม object ใน memory เช่น cart.getItems() แล้วเรียก item.getPrice() ทีละรายการ บนเครื่องเดียวอาจปกติ แต่ข้าม network กลายเป็น N+1 remote calls และ failure surface จำนวนมาก
Remote interface ควร coarse-grained ตามหน่วยงานที่มีความหมาย เช่น quoteCart(cartId) คืนข้อมูลที่ caller ต้องใช้ในรอบเดียว แต่ไม่ควรรวมใหญ่จน endpoint ทำทุกอย่างและเปลี่ยนตาม client ทุกตัว นี่คือสมดุลระหว่าง chatty กับ over-general API
| คำถามออกแบบ | เหตุผล |
|---|---|
| Operation เป็น command หรือ query | ช่วยกำหนด side effect, caching และ retry |
| ใครเป็น owner ของ invariant | ไม่กระจาย business rule ข้าม client |
| payload เป็น snapshot หรือ reference | กำหนด staleness และจำนวน call เพิ่ม |
| Error เป็น business rejection หรือ system failure | caller ตัดสินใจ retry/แก้ข้อมูลได้ถูก |
| Compatibility ช่วง rollout | client/server อัปเกรดไม่พร้อมกัน |
Error Model ที่ดี
Error ควรมี stable code, human message, retryability ที่ระวัง, correlation ID และรายละเอียดที่ไม่เปิดข้อมูลลับ อย่าให้ client parse ประโยค error เพื่อสร้าง logic และอย่าส่ง stack trace ภายในออกสู่ public boundary
Business error เช่นสินค้าหมดเป็นผลลัพธ์ที่ระบบเข้าใจ ไม่ควรปนกับ network unavailable ส่วน timeout เป็น ambiguity ไม่ใช่คำยืนยันว่า operation ไม่เกิด การแยกชนิดช่วยให้ caller เลือก compensate, retry, ask user หรือส่งงานตรวจภายหลังได้
13CORBA และ RMI: เรียนประวัติศาสตร์เพื่อเห็นขอบเขตของ Abstraction
CORBA ใช้ IDL และ ORB เพื่อข้ามภาษา/แพลตฟอร์ม ส่วน Java RMI ใช้ ecosystem ภาษาเดียว ทำ remote object และ serialization สะดวก ทั้งสองแสดงความพยายามสร้าง location/access transparency สูง แนวคิด stub, object reference, naming และ invocation semantics ส่งต่อมาถึง middleware สมัยใหม่
สิ่งที่เปลี่ยนคือระบบปัจจุบันมักลด distributed object ที่มี state ละเอียด แล้วใช้ stateless/coarse-grained service API มากขึ้น ใช้ schema ที่ชัดกว่า language object serialization และยอมเปิดเผย deadline/status/streaming แต่ gRPC ยังมี generated stub และ runtime เหมือนหลักเดิม ความใหม่จึงเป็นการเลือก trade-off ใหม่ ไม่ใช่การเกิดจากศูนย์
| บทเรียนจากอดีต | การประยุกต์ปัจจุบัน |
|---|---|
| IDL ทำ contract ข้ามภาษา | Protobuf/OpenAPI และ code generation |
| ORB ให้ location transparency | service discovery, client LB, mesh |
| Remote object reference | resource name, service endpoint, capability URL |
| Distributed GC ยาก | lease, TTL, explicit lifecycle |
| Fine-grained call แพง | coarse API, batching, streaming |
14gRPC สมัยใหม่: ความสามารถใหม่ ปัญหาเดิม และปัญหาเพิ่ม
HTTP/2 multiplexing ลด connection overhead และรองรับ unary, server streaming, client streaming และ bidirectional streaming Protobuf ให้ schema/type และ payload กระชับ แต่ streaming ไม่ได้ลบ backpressure; multiplexing อาจมี connection-level issue; และ binary protocol ทำให้ inspect ด้วยตายากขึ้น
Deadline, status code, metadata และ interceptor ทำให้ concerns ที่ RPC เก่าซ่อนไว้บางส่วนถูกเปิดเผยดีขึ้น อย่างไรก็ตาม auto-generated client ยังทำให้ remote call ดูง่ายจนสร้าง synchronous chain ยาวได้ Observability ต้องเก็บ trace ข้าม hop และ API review ต้องนับ fan-out
REST กับ gRPC ไม่ใช่การแข่งขันแบบผู้ชนะคนเดียว
| บริบท | REST/HTTP JSON | gRPC |
|---|---|---|
| Public/browser API | เข้าถึงง่าย tooling/caching กว้าง | browser ต้องมี bridge บางกรณี |
| Internal typed API | ยืดหยุ่นและอ่านง่าย | schema/codegen/streaming เด่น |
| Debug | curl และข้อความอ่านได้ | ใช้ grpcurl/reflection/tool เฉพาะ |
| Performance | เพียงพอสำหรับงานจำนวนมาก | payload/connection มีประสิทธิภาพ แต่ต้องวัด end-to-end |
15Workshop: ทำให้ Remote Call แสดงตัวตน
- สร้าง Client → Service A → Service B ด้วย REST หรือ gRPC ให้แต่ละ hop log trace/request ID และ deadline ที่เหลือ
- วัด baseline แล้วให้ B หน่วงแบบสุ่ม สร้าง latency distribution ไม่ใช่ค่าเดียว
- ตั้ง timeout ไม่สัมพันธ์กัน แล้วสังเกต work ที่ทำต่อหลัง client เลิกรอ จากนั้นแก้ด้วย deadline propagation
- ทำ B commit แล้วทิ้ง response ให้ A retry พิสูจน์ duplicate ก่อนเพิ่ม idempotency key
- เปิด retry ที่สองชั้นแล้วนับ amplification จากนั้นรวม policy ให้เหลือชั้นที่เหมาะ
- เปลี่ยน schema เพิ่ม optional field ให้ client เก่า/ใหม่ทำงานพร้อมกัน
- เปลี่ยน API fine-grained หลาย call เป็น coarse-grained call แล้วเปรียบเทียบ latency และ failure probability
16สรุปและขั้นตอนถัดไป
Remote Invocation คือ abstraction ที่ทรงพลังเพราะเปลี่ยน message protocol ให้เป็น interface ที่ใช้ง่าย แต่ความสำเร็จด้าน syntax เป็นเหตุให้เราเผลอลืมความต่างด้าน semantics Remote call มี latency, partial failure, serialization, version และ security boundary ที่ local call ไม่มี
หลักที่ควรจำคือทำให้ interface ใช้ง่ายโดยไม่ทำให้ต้นทุนและความไม่แน่นอนหายจากการตัดสินใจ Stub ซ่อน byte ได้ แต่ design ต้องเปิด deadline, idempotency, error และ compatibility ให้ชัด Proxy ช่วย retry ได้ แต่ไม่ควร retry side effect โดยไม่รู้ และ cancellation ช่วยหยุดการรอแต่ย้อน business commit ไม่ได้
ขั้นตอนถัดไปคือ Transactions & Concurrency Control เมื่อ remote call หลายคำสั่งเข้าถึง state เดียวกันพร้อมกัน เราต้องนิยามว่าผลลัพธ์แบบใดถือว่าถูกต้อง แล้วใช้ locking, optimistic control, timestamp หรือ MVCC เพื่อรักษา invariant โดยแลกกับ waiting, abort และ coordination