I've learned. I'll share.

June 27, 2008

My Experience with Message Passing Concurrency

I'm working on a peer-to-peer file synchronization program. It's really concurrent and distributed, and it's forced me to learn a thing or two about the concurrency models that we use as programmers. Over the past few years, I've tried both the shared-state model common to C, C++, Java, C#, Python, etc, and the message-passing model unique to Erlang and Scala. In my experience, the message-passing model (aka Actor Model) is far superior to the shared-state model for writing distributed applications. After having used both extensively, I'd go so far as to say that for distributed programming, shared-state concurrency is upside down and backwards.

Here's my informal proof that message-passing concurrency is necessary in a distributed system:

  1. Since the system in distributed, real shared state is impossible.
  2. The only way for the distributed components of an application to communicate is by sending a message from one to another.
  3. Everything else is an abstraction on top of message passing.
HTTP is an abstraction on top of message-passing. AJAX is an abstraction on top of HTTP on top of message-passing. XML-RPC is an abstraction on top of HTTP on top of message-passing. SOAP is an abstraction on top of an abstraction on top of an abstraction on top of HTTP on top of message-passing. Any RPC mechanism is an abstraction on top of message-passing. SQL queries to a remote database are an abstraction on top of message-passing. CORBA is a nasty, tangled mess on top of a foundation of message-passing.

See a pattern?

Not only are all of these abstractions, but they are leaky abstractions. Just about all RPC (Remote Procedure Call) frameworks try to pretend that remote objects or local. But the facade is impossible to keep from leaking. Calls to a remote object might fail or take arbitrarily long. If you want to make the same call to two different remote nodes, those calls must be made synchronously and sequentially; in order to call them in parallel on the remote nodes, they most be called in parallel on the local node. If a call to a remote node triggers a call back to the local node, which may trigger a call to a third node, you end up with a huge spaghetti mess of calls and threads.

I've been down that road. It wasn't pretty. There is a better way.

I think AJAX has opened our eyes a bit. It's a lot closer to message-passing than it is to RPC. In a REST architecture, you "send" messages by POSTing to a URL and you "receive" messages by GETing a URL. All messages are clearly local copies that were serialized and deserialized from the remote data. There isn't any leaky abstraction of data or classes pretending to be in two places at once. Data, timeouts, and failures are in your face and you have to deal with them. So, AJAX is a lot less leaky.

But AJAX is far from perfect. I can't help but think of the Greenspun's Tenth Rule of Programming with a twist on distributed programming: "Any sufficiently complicated distributed platform contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of a message-passing system (Erlang)". Once you get message-passing in your head, you can't help but think of AJAX in that way.

I've been down a better road. It was much more pleasant.

About six months ago, I rewrote major portions of our application with message-passing concurrency. I took a long time. It was tricky. It hurt my head. But it worked. It's a success. It's capable of things that the shared-state system could never do.

Having done it, I can emphatically say that if I could start all over, I would go with message-passing. Most of the work was building the infrastructure and wrapping my head around a new way of thinking. But now that I've done both of those, I've paid the costs and I'm reaping the rewards. We're moving the application in directions that would have been nearly in possible with the old concurrency model.

In my experience, message-passing concurrency is the best way to write distributed applications. But it isn't an easy road to go down. Support for it just isn't there in most programming languages and environments. So far, Erlang has been the pioneer. I would humbly agree that the way I've implemented message-passing in Python, compared to Erlang, looks like an ad-hoc, bug-ridden half-implementation. But for various reasons, I can't use Erlang. I'm hoping for someone to create Erlang++ or E# or Eython or something that combines the concurrency model of Erlang with a modern programming language. Until then, I'll just keep on cobbling together what I can onto the programming language I happen to be using.

More on that in another article.

32 comments:

  1. I've been working on actors in dynamic languages (currently Ruby and Python) for a couple of months, pretty much for the same reasons you do. I also envy the features and maturity of Erlang but I need actors in things that can't be reinvisioned in Erlang.

    For what it's worth:
    http://dramatis.mischance.net.

    ReplyDelete
    Replies
    1. Gia Thuận Shop địa chỉ hàng đầu chuyên cung cấp lồng đèn trung thu giá sỉ tốt nhất hiện nay trên thị trường. Ngoài ra chúng tôi còn cung cấp nhiều dịch vụ khác như dịch vụ in ấn. Trong đó phải kể đến dịch vụ in giấy khen giá rẻ tphcm... Chúng tôi còn cung cấp các loại đồ chơi khác nhau, nếu như đồ chơi, quà lưu niệm quá đặc biệt thì bạn có thể Pre order với chúng tôi.
      Hãy đến với chúng tôi để được phục vụ tốt nhất.

      Delete
  2. Wow. You on a very nice website with very nice documentation. It's obvious that you've spent some time on it. In comparison, my support and documentation is quite pathetic :). I also like the name name "dramatis".

    Your implementation of the Actor Model obviously has one huge difference with mine: yours implements synchronous message passing and mine implements asyncrhronous message passing. Since I'm working in a very distributed environment, asynchrounous message passing is MUCH, MUCH better for me. In fact, synchronous message passing has most of the same pitfalls as RPC and I don't think it's very suitable for disrtributed programming at all.

    But I think it's great that you're starting a whole project with nice support and documentation. The more popular and supported message passing becomes, the better.

    ReplyDelete
  3. Nice article Peter. It is good to see that someone has gone through much the same process as I have with message-passing. My dive into Erlang was largely inspired by the power I saw in AJAX.

    For a Python-like syntax and coding style on the Erlang VM check out Reia.

    However, after the initial weirdness shock with the syntax I found I actually preferred Erlang's native syntax to that of Java, Groovy, Ruby or Python. I found its pattern-matching and recursion support extremely useful when I was building a Mercurial SVN integration. I had initially tried to write the integration in Groovy and found the once familiar mutability of variables and types vs pattern-matching foreign, risky and lacking in expressive power.

    ReplyDelete
  4. Dramatis does have async calls; in fact, like Erlang gen_server, everything is async, just with a little support for making it easy to do rpc-like calls where that is the right semantics. Really it's continuation-passing, but a lot of people seem to be afraid of continuations. They're actually pretty important (though often hideable) in distributed programming.

    My experience has been that there are lots of cases in actor programs where you want to do these "pseudo-blocking" calls. You can always do them more explicitly, by having the target of the call send a message back. This is basically manual continuation passing and I've had to do it sometimes in the past. But, at least for me, I've found it so common, e.g., asking another actor for status, that if it's hard to express, it gets very aggravating very quickly. My code where I had to do this got ugly very fast and I've been hoping with dramatis, I'd not go down that path again.

    I agree fully that, used exclusively, rpcs aren't a very good programming model. They're the same old recursion model which doesn't provide any help in communicating between objects. But as far as I've seen, "blocking calls" are still needed in data-flow-like models in order to reflect data dependencies. (Actually, futures should work for that too, which I've been playing with with dramatis.)

    A simple example of this is the auction example, which I grabbed from Scala by Example. There's really nothing for a bidder to do until it hears the result of it's bid, so at least for that one exchange, CPS/RPC feels pretty natural, even in a distributed environment.

    ReplyDelete
  5. Instead of thinking of things as "RPC", I think of it as "make a request and wait for a reply". That way, you can do things like make multiple requests in parallel, choose what to do if the reply doesn't come in a certain amount of time, or keep doing other work until the reply. "RPC" is just one kind of possible request/reply pattern that allows only one request, hides timeouts, and doesn't allow work until the reply comes.

    One pattern for RPC that I do like that Erlang seems to use is to spawn another actor/process just for waiting for the reply. The main requester can then continue work, or make many requests, etc. But, obviously, we'd need to implement threadless actors for that to have any chance of working.

    ReplyDelete
  6. I also find the "RPC" label a lot less than a perfect fit. I've just had a hard time coming up with something that also isn't flawed. I try to avoid using "wait" in descriptions, because actors don't wait, exactly.

    Certainly any "rpc" in an actor system isn't going to be the same as traditional rpc's, a la ONC. How actor rpcs (see, there I go again ...) differ from ONC-like rpcs is an area I find pretty interesting. Erlang's OTP implements one pattern, with certain tradeoffs, dramatis a different pattern, etc.

    Is there an example where Erlang spawns an actor to wait for a reply? I can't remember any that highlight that in particular.

    I do find it interesting that Erlang gen_server "call" does pretty much block everything else. That feels kinda inconvenient. If you go back to low-level Erlang examples, there are plenty of cases where something like a status message would be allowed everywhere. But I don't think you can implement that with gen_server (because it implements the receive loop).

    Dramatis (which implements patterns from both Erlang and OTP) by default has that same behavior: when you make an rpc-like call, you become sensitive only to the response from that call. But it also allows the actor to specify that certain patterns will always be allowed, allowing, for example, status messages at any time, which is useful to me. I guess this means that all Dramatis actors are threadless: the threads are actually owned by the scheduler and are allocated to tasks to execute individual method calls as appropriate.

    Great discussion. I'm hoping for more of these: cross-fertilization, etc. I've set up a google group http://groups.google.com/group/actor-talk to maybe give people a place to collaborate?

    ReplyDelete
  7. How do you find Python-stackless then?

    Eve Online MMORPG use it heavily. I don't know about server, but for client-side for sure.

    ReplyDelete
  8. خدمات النظافة بالدمام لابد ان تقدمها شركة لديها ثقة في اتمام خدمات النظافة المنزلية لان النظافة العاية لا تعطي النتيجة الفعالة التي تتم بواسطة شركة تنظيف بالدمام متخصصة لانها تهتم بكل شئ داخل المباني فلدينا شركة تنظيف فلل بالدمام تكون متخصصة في نظافة الفلل المفروشة والمستعملة واعطائها الرونق الجمالي الخاص بها لدينا شركة تنظيف شقق بالدمام متخصصة في اعمال نظافة الشقق المنزلية وشقق المكاتب والمعارض بواسطة الادوات والمواد اللازمة لعمل ذلك لدينا تخصصتنا في اعمالنا التي ترضي العميل وتخلصة من الحشرات الصغيرة التي تسبب ازعاج كبير للاسرة من خلال شركة مكافحة حشرات بالدمام التي لديها معدتها وموادها لعمل ذلك كما يوجد متخصص ايضا للقضاء علي الحشرات من خلال التواصل مع شركة رش مبيدات بالدمام التي تنتهيك من كل الحشرات المنزلية كما نعمل معا للواصل اليك كل الخدمات المنزلية من خلال خدمة الاثاث بواسطة افضل شركة نقل اثاث بالدمام والتعامل معها سوف يصل الي منزلك الجيد بدون اي تعب
    كما يوجد خدمتنا الاخري التي يمكنك البحث عنها م والاستفادة منها
    شركة تنظيف بالقطيف
    شركة تنظيف بالجبيل
    شركة تنظيف بالاحساء

    ReplyDelete


  9. السلامه عليكم ورحمة الله وبركاته نحن فى شركة الكمال نقوم بافضل واقوى المبيدات العالميه

    الموجودة التى تقضى على جميع الحشرات الطائره والزاحفة وابادة الحشرات


    شركة مكافحة حشرات بالطائف



    شركة رش مبيدات بالطائف



    شركة مكافحة حشرات بجازان



    شركة رش مبيدات بجازان



    شركة مكافحة حشرات بحائل



    شركة رش مبيدات بحائل

    والسلامة عليكم وحمة الله وبركاته

    ReplyDelete
  10. In the name of God the Merciful We in the

    light of UK companies offer the best

    services in all cleaning Katenziv in

    apartments , boards and cabinets and

    Villas
    شركة

    نور المملكة

    شركة تنظيف بمكة
    And also we provide insect control for all

    insects flying and crawling and rat

    extermination
    شركة مكافحة حشرات بجازان
    We also offer moving furniture needed by

    everyone because of the expiration of the

    lease time or travel service
    شركة الصفرات لنقل الاثاث بالرياض
    And safety and God's mercy and blessings
    كما يوجد لدينا ايضا تنظيف المجالس والكنب والسجاد والموكيت باحدث الطرق الموجودة

    وافضل المواد التى تقضى البقع والاوساخ
    شركة تنظيف مجالس وكنب بحائل
    شركة تنظيف سجاد وموكيت بحائل
    وكل هذا بانسب الاسعار واقل التكلفة ونحن نعمل فى خدماتكم 24ساعه

    ReplyDelete
  11. This blog is so nice to me. I will continue to come here again and again. Visit my link as well. Good luck
    cara menggugurkan kandungan
    cara menggugurkan kandungan

    ReplyDelete
  12. شركة تنظيف خزانات بالدمام
    التطور المستمر للشركة سواء من الناحية التكنولوجية أو الميدانية فكلاً منهما يعطي فائدة كبرى للأخرى، حيث تجد أن التكنولوجيا الحديثة التي تستخدمها شركة تنظيف خزانات بالدمام بالمعدات والأجهزة سهلت الأمر كثيراً في ميدان العمل عند تنظيف الخزانات شركة عزل خزانات بالدمام
    شركة تنظيف خزانات بالخبر
    مراعاة الشركة للمعايير والمقاييس الخاص بالسلامة لإعطاء أفضل النتائج، وما كان لنا سوى أن نهتم بذكر تلك النقاط عن كيفية عمل شركة غسيل خزانات بالقطيف شركة تنظيف خزانات بالقطيف

    ReplyDelete
  13. شركة عزل اسطح بالدمام
    عزيزي العميل نرحب بكم في شركة كشف تسربات المياه بالجبيل هل تعاني من إرتفاع ... شركة كشف تسربات المياه بالخبر شركه تسربات  شركة كشف تسربات المياه بالجبيل
    شركة كشف تسربات المياه بالخبر
    هل تعبت عن البحث على شركة كشف تسربات المياه بالجبيل انتا الان مع شركة الافضل ... شركة كشف تسربات المياه بالدمام هى واحده من افضل الشركات شركة كشف تسربات المياه بالدمام

    ReplyDelete
  14. ان حشرة البق تشكل خطورة كبيرة علي المنازل وحصوصا الاطفال لذلك يمكنك ايجاد الحل الامثل من خلال شركة مكافحة البق بالدمام حيث تضم الشركة افضل الخبراء فهي افضل شركة مكافحة البق بالدمام شركة رش حشرات بالدمام
    شركة مكافحة حشرات بالدمام
    شركة مكافحة حشرات بالخبر تقدم لكم العديد من الخدمات المميزة، حيث أن الشركة تستطيع مكافحة النمل الابيض بالقطيف بالإضافة إلى كافة أنواع الحشرات الأخرى بكل سهولة وبأحدث الإمكانيات مثل مكافحة البق بالقطيف ومكافحة الصراصير بالقطيف ومكافحة الصراصير بالقطيف ولها فروع مثل شركة مكافحة حشرات بالدمام شركة مكافحة حشرات بالخبر
    شركة مكافحة البق بالدمام

    ReplyDelete
  15. يمكن تعريف الشحن المحدود الذي تقدمه شركه نقل اثاث بالدمام بكونه ما يقتصر على نقل ما قد يحتاجه العميل من أثاث كالأجهزة الكهربائية أو الأثاث الخشبية حتى وإن كان لقطع غيرة ومدودة منه. شركة نقل عفش بالدمام
    شركة نقل اثاث بالدمام
    فريق عمل شركة نقل عفش بالدمام نعرف جميعًا أن موظفي وعُمال الشركة هم الأيد المُحركة لها، والتي تلعب الدور الأكبر في مدى نجاحها واستمراره أرخص شركة نقل عفش بالدمام

    ReplyDelete
  16. شركة نقل اثاث بالخبر
    دور شركة نقل أثاث بالقطيف في عملية نقل العفش شركة نقل عفش بالخبر
    شركة نقل اثاث بالقطيف
    أن العشوائية والعمل بدون تخطيط ووعي يكون مصيرهُ النهائي الفشل التام لا محالة من ذلك، لذلك فقد عمدت شركة نقل عفش بالخبر على أن تعمل وفقًا لاستراتيجية مُحددة شركة نقل عفش بالقطيف
    شركة نقل اثاث برأس تنورة

    ReplyDelete
  17. التغليف للأجهزة الكهربائية يتم كما لو أنك قمت عميلنا الكريم بشراء الجهاز جديد، في البداية نقوم فى شركه نقل عفش بالظهران بفصله ومن ثم تنظيفه ومن ثم التغليف باستخدام الفلين والذي يقوم بإحاطة الجهاز مع ضمان عدم تعرضه للكسر أو الخدش ومن ثم يتم وضعه في أحد الصناديق ذات الحجم المناسب له ومن ثم نقوم بربطه بالحبال. شركة نقل عفش بالدمام
    شركة نقل اثاث بالدمام
    افضل شركة نقل اثاث بالدمام وشركه نقل عفش بالخبر والقطيف  والامر ليس قاصرا على الدمام  وانما لدينا فروع اخرى بالجبيل والقطيف والاحساء  و نحن من أوائل الناس كــ شركة نقل عفش بالخبر والدمام لدينا خبرة متطورة في خدمات نقل اثاث السكنى والتجاري والفندقي بكل عناية ومهنية لعملائنا الكرام بالاعتماد على تكنولوجيا النقل من الباب إلى الباب باستخدام أحدث الآليات في النقل لضمان أمان خدمة النقل في اقل وقت واقل تكاليف أرخص شركة نقل عفش بالدمام

    ReplyDelete
  18. قبطان الخليج -0504353061-يُعد الأثاث من أكثر الأشياء التي تحتاج إلى حرص ودقة بالغة عند القيام بنقلها شركة نقل عفش بالظهران
    تحرص على تقديم الضمان اللازم عن العمل الذي تقوم به لعملائها الكرام، فالضمان بدوره يطمئن العميل وفي النفس الوقت يضمن مصداقية الشركة شركة نقل اثاث بالظهران

    ReplyDelete
  19. ان حشرة البق تشكل خطورة كبيرة علي المنازل وحصوصا الاطفال لذلك يمكنك ايجاد الحل الامثل من خلال شركة مكافحة البق بالدمام حيث تضم الشركة افضل الخبراء فهي افضل شركة مكافحة البق بالدمام شركة مكافحة البق بالدمام
    شركة مكافحة البق بالخبر
    شركة مكافحة النمل الابيض بالدمام
    افضل شركة مكافحة النمل الابيض بالقطيف شركة  انوار طيبة ونصلك اينما كنت نستخدم افضل المبيدات للقضاء علي الحشرات النمل والصراصير وبق الفراش والوزع والنمل الابيض وجميع الحشرات الزاحفة ,مكافحة جميع انواع الحشرات باستخدام افضل المبيدات المستوردة والمحلية لضمان القضاء علي الحشرات مع الضمان وخصومات هائلة شركة مكافحة النمل الابيض بالقطيف
    شركات مكافحة حشرات ورش مبيدات بالدمام هل تبحث عن شركة مكافحة حشرات بالسعودية هل لديك نمل في المطبخ او حشرة البق في الخشب؟ هل عندك صراصير او نمل او فئران وتفسد عليك حياتك في منزلك او شركتك؟ لا تقلق فهناك العديد من شركات مكافحة الحشرات في السعودية وأيضا يوجد العديد من شركة رش مبيدات ومكافحة الحشرات بالدمام شركة رش مبيدات بالدمام

    ReplyDelete
  20. شركة مكافحة حشرات بالخبر تقدم لكم العديد من الخدمات المميزة، حيث أن الشركة تستطيع مكافحة النمل الابيض بالقطيف بالإضافة إلى كافة أنواع الحشرات الأخرى بكل سهولة وبأحدث الإمكانيات مثل مكافحة البق بالقطيف ومكافحة الصراصير بالقطيف ومكافحة الصراصير بالقطيف ولها فروع مثل شركة مكافحة حشرات بالدمام شركة مكافحة حشرات بالقطيف
    شركة مكافحة حشرات بالدمام. ان شركة مكافحة حشرات بالدمام تعلم جيدا ان الحشرات من أكثر الكائنات الحية انتشارا وتواجد على سطح الأرض حيث انها تعيش وتتكيف في المناخات المختلفة وتعرف شركة رش مبيدات بالدمام ان بعض الحشرات يعيش في جميع الاماكن فبعض الحشرات تستطيع الطيران فذلك يجعلها تنجو ولكن تفضي عليها من خلال افضل شركة مكافحة حشرات بالمنطقة الشرقية ، شركة الشرق الاوسط للنظافة ومكافحة الحشرات شركة مكافحة حشرات بالخبر
    شركة مكافحة حشرات بالدمام
    شركة مكافحة حشرات برأس تنورة

    ReplyDelete
  21. شركة تنظيف كنب بالدمام عندما تريد نظافه الكنب الموجود بمنزلك لاتنزعج اليك أفضل شركه تنظيف مكيفات بالدمام باحدث المكينات وافضل طرق التنظيف افضل شركة تنظيف كنب بالدمام نحن بعون الله من اكبر شركات التنظيف الكنب والمجالس بالدمام والخبر والقطيف والجبيل متخصصون فى مجال تنظيف جميع الاقمشه من ستائر ومجالس شركة تنظيف كنب بالدمام
    شركة غسيل كنب بالدمام
    افضل شركة تنظيف بالدمام و بالخبر ارخص الاسعار ، لنظافة المنازل و الشقق اهمية كبيرة في صحة الاسرة ، ولذلك اخترنا في الوطنية للنظافة بالمنطقة الشرقية افضل شركة تنظيف بالدمام و بالخبر ان نقدم لاهل الدمام والخبر خدمات تنظيف شاملة مع التعقيم لكافة مكونات المنزل العصري من اثاث او مفروشات او حمامات و مطابخ وتنظيف سيراميك وجلى بلاط شركة تنظيف كنب بالقطيف

    ReplyDelete
  22. انوار طيبة أفضل شركة نظافة بالدمام متخصصة في مجال التنظيف بالدمام والمناطق المجاورة فنحن شركة تنظيف مكيفات بالدمام نقوم أيضاً بتنظيف (فلل– شقق- منازل- بيوت- المجالس- العمائر- الشركات – المطاعم – الفنادق- المحال التجارية) وجميع الأماكن التي تحتاج للتنظيف، وتقوم الشركة بتنظيف منزلك بأحدث الماكينات شركة تنظيف مكيفات بالدمام
    شركة تنظيف مكيفات بالخبر
    شركة تنظيف بالخبر تقدم أسعار مميزة لعملائها فهي أرخص شركة تنظيف مكيفات حيث تقوم بتنظيف المنازل والشقق والفلل والكنب والسجاد والمسابح والمدارس والمساجد والخزانات بالدمام و بالخبر و بالجبيل و بالقطيف تستخدم أجود أنواع المنظفات فهي افضل شركة نظافة بالخبر شركة غسيل مكيفات بالدمام
    شركة تنظيف مكيفات بالقطيف
    مؤسسة انوار طيبة-ارخص شركة تنظيف مكيفات بالقطيف والدمام والجبيل – افضل شركة تنظيف مكيفات السبلت بالدمام والقطيف، وتنظيف وصيانة وغسيل المكيفات سبليت ومركزية ومخفي وكاست ودولابي وشباك وتركيب جميع المكيفات شركتنا افضل شركة غسيل مكيفات بالدمام والجبيل تتعامل معكم شركة تنظيف مكيفات برأس تنورة

    ReplyDelete
  23. يمكن تعريف الشحن المحدود الذي تقدمه شركه نقل اثاث بالدمام بكونه ما يقتصر على نقل ما قد يحتاجه العميل من أثاث كالأجهزة الكهربائية أو الأثاث الخشبية حتى وإن كان لقطع غيرة ومدودة منه. شركة نقل عفش بالخبر
    نقل عفش بالخبر
    شركة نقل عفش فى بالخبر
    وجدير بالذكر أنه بعد الرفع يقوم العمال بتركيب الأثاث كما كان شركات نقل العفش بالخبر

    ReplyDelete

  24. عالميه الدمام- 0542613483 - افضل شركات نقل العفش بالمنطقه الشرقيه شركة نقل عفش بالجبيل
    هل تبحث عن شركة مناسبة للقيام بنقل عفش منزلك، هل تخشى من تجارب الأصدقاء المؤلمة مع شركات نقل العفش؟ شركة نقل عفش بالقطيف
    لا تقلق عزيزي العميل، فأنت الآن في المكان الصحيح، حيث جاءت إليك شركة نقل عفش بالدمام شركة نقل اثاث بالخبر

    ReplyDelete
  25. شركة نقل عفش بالدمام
    عالميه الدمام- 0542613483 - افضل شركات نقل العفش بالمنطقه الشرقيه شركة نقل اثاث بالدمام
    ارخص شركة نقل اثاث بالدمام
    شركة نقل اثاث بالجبيل
    أن الشركة تتمتع بالالتزام التام بمواعيدها، فلا تعمل على مخالفتها تحت أي ظرف من الظروف وهذا بداية من عملية الاتصال وحتى إنهاء الخدمة تمامًا، كما أن فريق العمل ذو حجم كبير نسبيًا نقل عفش بالخبر
    شركة نقل عفش بالجبيل

    ReplyDelete

Blog Archive

Google Analytics