The cause is the following:
In Action 1 of createLedger, 4 calls to zk are fired off : one getChildren() and 3 create()s. The callback for the getChildren() moves the op into Action 2 which is not correct because the 3 creates are still pending. Now the create callback just assumes that its already in action 2, and hence advances to action 3. Now the same op is enqueued twice, and its action has been set to 3. The double queuing explains why the callback is called twice.
Since in this sequence of events action 2 is skipped altogether, and we end up with 0 bookies. If the dequeuer actually happens to dequeue before the action is set to 3, then action 2 will also be carried out which explains why we get 0 bookies only sometimes and not always (which explains
In general, this style of asynchronous programming with stage numbers is error-prone, and hard to read. Object creation is cheap, and operations like openLedger and createLedger are rare. Why not just create anonymous inner classes as callbacks instead of doing this state machine?