[openstack-dev] [Heat] Convergence proof-of-concept showdown
Zane Bitter
zbitter at redhat.com
Mon Dec 8 22:19:45 UTC 2014
On 08/12/14 07:00, Murugan, Visnusaran wrote:
>
> Hi Zane & Michael,
>
> Please have a look @ https://etherpad.openstack.org/p/execution-stream-and-aggregator-based-convergence
>
> Updated with a combined approach which does not require persisting graph and backup stack removal.
Well, we still have to persist the dependencies of each version of a
resource _somehow_, because otherwise we can't know how to clean them up
in the correct order. But what I think you meant to say is that this
approach doesn't require it to be persisted in a separate table where
the rows are marked as traversed as we work through the graph.
> This approach reduces DB queries by waiting for completion notification on a topic. The drawback I see is that delete stack stream will be huge as it will have the entire graph. We can always dump such data in ResourceLock.data Json and pass a simple flag "load_stream_from_db" to converge RPC call as a workaround for delete operation.
This seems to be essentially equivalent to my 'SyncPoint' proposal[1],
with the key difference that the data is stored in-memory in a Heat
engine rather than the database.
I suspect it's probably a mistake to move it in-memory for similar
reasons to the argument Clint made against synchronising the marking off
of dependencies in-memory. The database can handle that and the problem
of making the DB robust against failures of a single machine has already
been solved by someone else. If we do it in-memory we are just creating
a single point of failure for not much gain. (I guess you could argue it
doesn't matter, since if any Heat engine dies during the traversal then
we'll have to kick off another one anyway, but it does limit our options
if that changes in the future.)
It's not clear to me how the 'streams' differ in practical terms from
just passing a serialisation of the Dependencies object, other than
being incomprehensible to me ;). The current Dependencies implementation
(1) is a very generic implementation of a DAG, (2) works and has plenty
of unit tests, (3) has, with I think one exception, a pretty
straightforward API, (4) has a very simple serialisation, returned by
the edges() method, which can be passed back into the constructor to
recreate it, and (5) has an API that is to some extent relied upon by
resources, and so won't likely be removed outright in any event.
Whatever code we need to handle dependencies ought to just build on this
existing implementation.
I think the difference may be that the streams only include the
*shortest* paths (there will often be more than one) to each resource. i.e.
A <------- B <------- C
^ |
| |
+---------------------+
can just be written as:
A <------- B <------- C
because there's only one order in which that can execute anyway. (If
we're going to do this though, we should just add a method to the
dependencies.Graph class to delete redundant edges, not create a whole
new data structure.) There is a big potential advantage here in that it
reduces the theoretical maximum number of edges in the graph from O(n^2)
to O(n). (Although in practice real templates are typically not likely
to have such dense graphs.)
There's a downside to this too though: say that A in the above diagram
is replaced during an update. In that case not only B but also C will
need to figure out what the latest version of A is. One option here is
to pass that data along via B, but that will become very messy to
implement in a non-trivial example. The other would be for C to go
search in the database for resources with the same name as A and the
current traversal_id marked as the latest. But that not only creates a
concurrency problem we didn't have before (A could have been updated
with a new traversal_id at some point after C had established that the
current traversal was still valid but before it went looking for A), it
also eliminates all of the performance gains from removing that edge in
the first place.
[1]
https://github.com/zaneb/heat-convergence-prototype/blob/distributed-graph/converge/sync_point.py
> To Stop current stack operation, we will use your traversal_id based approach.
+1 :)
> If in case you feel Aggregator model creates more queues, then we might have to poll DB to get resource status. (Which will impact performance adversely :) )
For the reasons given above I would vote for doing this in the DB. I
agree there will be a performance penalty for doing so, because we'll be
paying for robustness.
> Lock table: name(Unique - Resource_id), stack_id, engine_id, data (Json to store stream dict)
Based on our call on Thursday, I think you're taking the idea of the
Lock table too literally. The point of referring to locks is that we can
use the same concepts as the Lock table relies on to do atomic updates
on a particular row of the database, and we can use those atomic updates
to prevent race conditions when implementing
SyncPoints/Aggregators/whatever you want to call them. It's not that
we'd actually use the Lock table itself, which implements a mutex and
therefore offers only a much slower and more stateful way of doing what
we want (lock mutex, change data, unlock mutex).
cheers,
Zane.
> Your thoughts.
> Vishnu (irc: ckmvishnu)
> Unmesh (irc: unmeshg)
More information about the OpenStack-dev
mailing list