dash2 has asked for the wisdom of the Perl Monks concerning the following question:

Warning: partly this is a blatant plug for Net::Distributed ... I am feeling unloved. I promise to answer some newbie questions if someone will look at my newbie code. Anyway, I also think it is an interesting question.

OK. So I have a Net::Distributed class, which allows peers to talk to each other. The class itself is (1) a facade which is easy for clients to talk to, and (2) a transport which provides methods for sending and receiving messages. To handle messages, you load Net::Distributed::Handler objects. These may also export some utility methods... for example a Net::Distributed::Handler::Joiner will provide a send_join_request method as well as doing the handle_join_request from within its own package.

I want to let nodes migrate. That is, if a node is about to be destroyed, it should be able to nip off to another peer, fork and start running at a new address. Groovy. (Assume that I have the security issues covered ->koff<-)

So I've created a Net::Distributed::Handler::Migrator object. It handles migration requests, and also provides default methods for migrating, and for setting up a child. So far so good.

The problem is that the child has to have a new address. It can't have exactly the same address as the old one (otherwise messages would get to them both.) Only the Net::Distributed object can provide the address, because only it knows what an address is - it provides the message transport layer, remember? But if I add these class-specific methods into Net::Distributed, I am adding a load of code that is not really relevant unless you want to use a migration handler. And that's bad, and will make Net::Distributed hard to subclass. There are other parts of the migration process that are also specific to the Net::Distributed object.

So does anyone see a good way to handle this? How can I provide migration services, with a simple client interface, which still Do The Right Thing when a client migrates?

bw

dave hj~

Replies are listed 'Best First'.
Re (tilly) 1: Organizing my packages
by tilly (Archbishop) on Feb 06, 2002 at 02:37 UTC
    It is too bad that nobody got around to answering this question (server problems can't have helped). I don't really have an answer either, but I do have some basic ideas.

    The basic problem with getting this migration to work is being able to name your resource uniformly and have the same naming work before and after. Therefore I would suggest breaking out the naming logic into an object which Net::Distributed proxies off of, and then have a migration module. There is (as you note) a strong tie between the migration infrastructure and the naming object. There is no issue for children of the Net::Distributed other than the fact that they may need to implement some methods that the migration module will expect. (Note: Storable is likely to be massively helpful for you.)

    In order for migration to be able to work you need to have your naming authority set up, persistent, with everyone looks to them for names. When an object wants to migrate it tells the naming authority to delay any name resolutions, it spawns the new child, tells the naming authority where the resource is to be found now, and then exits. Clients just block during the migration process then find the object at the new location.

    The naming authority could be something as simple as a relational database. It could be a custom server.

    The advantage of the design I proposed is that if someone doesn't want to support migration, they can use a simpler and faster design. If someone thinks it should work differently than it does, they can subclass that proxy object and the difference will ripple properly through all subclasses of Net::Distributed that they might have.

    Whether or not you like this design, my suggestion for a good places to look for inspiration are clustering projects like Mosix. Their overall design may differ in key respects, but they have to face the same issue and are likely to have good advice about issues they faced.

      Interesting solution.

      Actually there are two problems here:

      1. a specific issue with migration methods - need to provide new addressses. I got round this by simply having the Migrator handler initialized with an array of spare addresses, which are popped off to migrated children. So the handler itself is the naming authority.

      2. a general issue with coupling. I came to the conclusion that Handler objects need to be tightly coupled with the Distributed object, which is basically just a facade. Handler objects supply all the functionality for handling messages, so they need to be able to send messages back, etc.

      So now I initialize Handlers with the Distributed object which uses them. If an object requires special behaviour to use a handler, you subclass the handler and provide the special behaviour from the Distributed object's public methods.

      Example: to serialize using Storable, I need to undef a HTTP::Daemon object in Distributed::Transport, because it has a blessed socket which I can't serialize. Of course, if I am using a different transport, this won't apply. Rather than adding migration specific stuff to Net::Distributed::Transport, I can just subclass the Migrator to be Migrator::HTTP, and to automatically undef the Daemon and bring it back to life after subclassing.

      I don't think there is a better solution than this. To provide object-specific behaviour, you either subclass the object, or subclass a related handler. As Distributed is meant to be a facade, subclassing it feels wrong.

      Thanks for your comments. Net::Distributed has had some changes which I will upload shortly; Net::Distributed::Space is coming soon... woohoo.

      dave hj~