الأساسيات · فصل ٤

الـ Encoding والتطور: الداتا وهي بتتنقل

Compatibility · Protobuf · Avro · RPC · message brokers
🎯 الهدف: تغيّر السيستم والقديم والجديد لسه بيفهموا بعض، من غير ما الداتا تضيع.

القديم والجديد عايشين مع بعض

التطبيق بيتغير والـ schema بتتغير، بس مش في لحظة واحدة. السيرفر بيتعمل له rolling upgrade: الكام نود الجداد ينزلوا من غير downtime. والموبايل ممكن مايتحدّثش لشهور.

⬅ Backward

الكود الجديد يقرا داتا كتبها كود قديم. أسهل نسبيًا: أنت عارف الفورمات القديم.

➡ Forward

الكود القديم يقرا داتا كتبها كود جديد. أصعب: لازم يتجاهل اللي مايعرفوش.

الاتنين لازم يشتغلوا مع بعض طول فترة النشر.

أشكال الـ encoding

في الرام الداتا objects. على الديسك والشبكة لازم تبقى بايتات. التحويل encoding، والعكس decoding.

فورمات لغة واحدة (Java Serializable، pickle): مربوطة بلغة، والـ decoding يقدر ينفّذ كود، والـ versioning متهمل. تجنّبها على الحدود.

JSON و XML و CSV: مقروءين ومنتشرين، بس:

  • الأرقام بتتلخبط. JavaScript بيبوّظ الأرقام الأكبر من ٥٣ بت، عشان كده فيه APIs بترجع الـ ID رقم ونص.
  • الـ binary مش مدعوم إلا Base64 وده بيزوّد الحجم.
  • الـ schema اختيارية، والـ CSV مفيش اتفاق ثابت على الـ escaping.

كويسين للتبادل بين شركات. جوه السيستم، الفورمات الثنائي بـ schema أوفر.

Thrift و Protobuf: schema إجباري، وبدل اسم الحقل بيتكتب رقم (tag). تغيّر الاسم تمام لأن الاسم مش في الداتا. تغيّر رقم الـ tag يبوّظ كل القديم. حقل جديد لازم يكون optional عشان القديم يتقري. متستخدمش رقم tag اتحذف تاني أبدًا.

Avro: مفيش tags في الداتا خالص. الـ reader بيقارن writer’s schema بـ reader’s schema بالاسم. حقل في الكاتب مش عند القارئ يتجاهل. حقل عند القارئ مش في الكاتب ياخد default. ممتاز لما الـ schema تتولد من جدول لوحدها، ومش محتاج code generation.

الداتا بتمشي إزاي؟

عبر الداتابيز. اللي بيكتب واللي بيقرا ممكن يكون أنت في المستقبل. مع الـ rolling upgrade، نسخة قديمة ممكن تقرا داتا نسخة جديدة، فالـ forward compatibility لازم.

الداتا بتعيش سنين والكود بيتبدل في دقايق. الـ migration الكامل نادر. الداتابيز بتسيب القديم زي ما هو.

عبر الخدمات. REST أنسب للـ public APIs: URLs و cache و curl. الـ RPC بيحاول يخلي نداء الشبكة شكله function call محلية، وده وهم:

  • النداء المحلي يا نجح يا فشل. الشبكة ممكن timeout ومتعرفش حصل إيه.
  • الـ retry ممكن ينفّذ مرتين، فلازم idempotence.
  • الوقت مش متوقع، ومينفعش تبعت pointers.
  • الطرفين ممكن يبلغات مختلفة.

gRPC أسرع جوه الشركة. السيرفرات بتتحدث الأول، فلازم backward في الريكوست و forward في الرد.

عبر الرسايل. الوسيط (Kafka، RabbitMQ) بيفصل المرسل عن المستقبل: buffer لو المستقبل مش متاح، وإعادة إرسال، ورسالة واحدة لكذا مستقبل. الـ actor model (Akka، Erlang) كل actor ليه state ومش بيشاركه، وبيفترض أصلًا إن الرسايل ممكن تضيع.

Checklist الـ encoding

🧠 اختبر نفسك
١) ضفت field جديد required في Protobuf. إيه اللي هيحصل؟
الكود الجديد هيفشل يقرا الداتا القديمة اللي مفيهاش الحقل. كده كسرت الـ backward compatibility.
٢) ليه Avro مش محتاج tag numbers؟
لأنه بيطابق writer’s schema مع reader’s schema بأسماء الحقول.
٣) إيه مشكلة retry في RPC؟
الطلب الأول ممكن يكون اتنفذ والرد هو اللي ضاع، فيتنفذ مرتين. لازم idempotency.
٤) ليه فورمات اللغة الواحدة وحشة على حدود الخدمات؟
مربوطة بلغة، والـ decoding ممكن ينفّذ كود، والـ versioning ضعيف.