[Introduction]
        We had a nice discussion on #linux-cluster today about using the
retrieving configuration from the objdb.  Here's my attempt to describe
both viewpoints and how we can mesh them together.  I'll speak in terms
of two developers, let's call them "Fabio" and "Steve".  Please note:
Where I'm wrong, it's all my fault.
        I'm deliberately writing this before reading the "API take 2"
email - I want to express what I got out of the discussion, not comment
on that email.  I apologize for writing out tons of pseudocode where
simple english could suffice - we were having enough confusion that I
want to spell it out.

[Fabio]
        There's a guy named Fabio.  In between his busy schedule of
posing for romance novel covers, he likes to hack on cluster software.
He makes simple applications, nothing fancy, but its fun.  His latest
application only needs a couple pieces of configuration, and he'd like
to use the objdb to store it.  It seems much better than writing his own
configuration file, etc.
        The first piece of configuration is a global timeout for the
application.  It's one value shared by all participating nodes.  Pretty
simple stuff.  The second piece is a port number where each node is
listening for some out-of-band communication.  Each node wants the list
of ports his fellows are on.  Pretty simple stuff.  So, Fabio would like
to ask the following questions of objdb:

    1) What is the global port value?
    2) For each node, what is its listening port?

        Implied in this "simple" query is that Fabio doesn't want to
learn some fancy way of asking the questions.  His application has to be
done before he jets off to Milan for another shoot.  He already knows
XPath, and it satisfies his needs to specify the query.  The response
can come back as a string as well - he knows how to use strtol(3).  In
an ideal world, he'd be able to use the timeout like so:

    fabio_app_init()
    {
        query("/global/timeout", &timeout);
        fabio_app_set_timeout(timout);
    }

        It's very simple and linear and reads exactly like you would
want.  Of course, the world isn't that clean - how does "query()" know
what to talk to, etc.  So there has to be some connection handling, etc.
        What about the port values?  Well, he needs to get a list of
nodes, then ask for their ports.  We're talking about openais here, so
certainly we can go with an iterator:

    fabio_app_init()
    {
        ...
        query_iterate("/nodes/child::*", &iterator);
        while (iterator_next(iterator, &nodename)) {
            query("/nodes/$nodename/@port", &port);
            portnum = strtol(port, NULL, 10);
            fabio_app_set_node_port(nodename, portnum);
        }
    }

        Again, we're using pseudocode.  You need a connection to the
configuration system, etc.  But this is the basic flow that Fabio's
application needs.  It's very easy to read and to program.  Fabio is
done, and he's off to Milan.
        The generic theme here is that Fabio wants to ask for a value or
a list of values, and he wants to have those values when the function
call returns.

[Steve]
        Steve works on clustering full-time; he doesn't model except for
trade magazines.  He's busy on a service application that wants to be
very dynamic.  The nodes form a pool of service processes, and dispatch
code sends work to each node as needed.  Optimum performance can be had
by sending work to nodes that aren't busy.  So each node will store its
workload in the objdb.  This will change on the fly as nodes take up
work and then finish it.  The dispatch code doesn't want to spend any
time talking to the object database; it wants to be notified when
workload values change.
        Steve's needs are pretty simple:

    1) When a value changes, please poke the application with the news.

Steve will handle everything else from there.  Here's how he wants to
code it:

    void callback(object, key, value)
    {
        nodename = object;
        if (key == "workload") {
            workload = value;
            steve_app_set_node_workload(nodename, workload);
        }
    }

    steve_app_init()
    {
        set_callback(callback);
        watch_for_changes("/nodes/$nodename/@workload");
    }

    steve_app_dispatch(work)
    {
        foreach(nodename, nodes) {
            if (steve_app_get_node_workload(nodename) < THRESHOLD) {
                steve_app_send_work(nodename, work);
                break;
            }
        }
    }

        Once again, we're ignoring the connection to the objdb.  I'm
handwaving how "object, key, value" match to "$nodename, workload,
$workload".  They map somehow.
        The core thing Steve wants here is that every time $workload
changes, he wants callback() called.  He doesn't want to wait on the
query.  In this case, he doesn't even want to *ask* the query twice.
        Once he's doing it this way, he can use the callback for
one-off queries too.  Let's say he adds a timeout.  Where Fabio's
program did:

    query("/global/timeout", &timeout);

Steve's program does:

    void callback(object, key, value) {
    {
        if (object == "global") {
            if (key == "timeout") {
                steve_app_set_timeout(vaulue);
            }
        else {
            /* workload code from above */
        }
    }

    steve_app_init()
    {
        register_callback(callback);
        watch_for_changes("/nodes/$nodename/@workload");
        query_async("/global/timeout");
    }

        The "watch_for_changes()" functions sets up notification in
perpetuity, while the "query_async()" is a one-time deal.
        Since Steve is already using a callback scheme, asynchronously
querying the timeout fits right in.  His application sees objdb as one
big state-change notifier.  Heck, he might not even want to use XPath to
define his queries, as objdb supports more complex types.  Most
people will want the simplicity of XPath and strings, though.

[Joel]
        Lastly, there's a guy named Joel.  He generally gets his nose in
others' business; basically he's meddlesome.  Here, he thinks Steve has
a great dynamic application.  Steve's callback is pretty darned good.
But Fabio's dream API is pretty nice too - it's very simple when simple
is all that is required.  It would be awful for Fabio to have to code up
a whole callback scheme to get his values.  Not only is it more work for
Fabio, but it makes the code harder to read, understand, and debug.
        Enough with the third person.  I see no reason these API cannot
coexist.  In fact, they are just layers atop each other.  While there is
a question of who implements which layer, I think we've settled that a
core library should present them so that we can drop extrinsic
libraries like libcman (which is doing the API for Fabio right now).
        What does everyone think?  Did I capture the ways we are seeing
objdb access?

Joel

-- 

"Friends may come and go, but enemies accumulate." 
        - Thomas Jones

Joel Becker
Principal Software Developer
Oracle
E-mail: [EMAIL PROTECTED]
Phone: (650) 506-8127
_______________________________________________
Openais mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/openais

Reply via email to