[Tutor] Starbucks does not use two-phase commit

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Fri Jan 20 20:34:42 CET 2006



On Fri, 20 Jan 2006, Bernard Lebel wrote:

> So have written a little test script. The fact is that I want to be able
> to manage the same queue from separate threads. Below is an example of
> what my real program is doing:


Hi Bernard,

One problem is that there's a single outputQueue being presented to get
results back from the Server.

A different approach is to use a lot of outputQueues.  *grin*

The idea is that when we send a job submission, we immediately get back a
"ticket".  We can then use this ticket to claim() our result.  Each ticket
is unique to a job submission, so we shouldn't see any bleeding going on
between clients.


Here's some code that implements this idea.  It's a little complex, so you
may want to read through it slowly:


################################################
from threading import Thread
from Queue import Queue


class Ticket:
    """A small token that we can use to claim our result."""
    def __init__(self, q):
        self.q = q
        self.result = None
        self.done = False

    def claim(self):
        if not self.done:
            self.result = self.q.get()
            self.done = True
        return self.result


class Server:
    _QUIT_JOB = ['Quit!']

    def __init__(self):
        """A queue will contain 2-tuples of (job, outputQueue)
        elements."""
        self.queue = Queue()


    def startServer(self):
        """Brings the server online."""
        Thread(target=self._jobLoop).start()


    def schedule(self, job):
        """Schedules a job to be done and returns a ticket that the
        client can use later to claim the result of the job."""
        outputQueue = Queue()
        self.queue.put((job, outputQueue))
        return Ticket(outputQueue)


    def scheduleShutdown(self):
        """Add a 'job' that shuts the system down."""
        self.queue.put((Server._QUIT_JOB, None))


    def _jobLoop(self):
        """Continue looping through tasks."""
        while True:
            print "Looping..."
            (nextJob, outputQueue) = self.queue.get()
            if nextJob is Server._QUIT_JOB:
                return
            returnValue = self._doJob(nextJob)
            outputQueue.put(returnValue)


    def _doJob(self, job):
        print "I'm doing", job
        return job + job ## something to show that we're doing something



def separateCaller(server):
    for i in range(1000, 1004 + 1):
        print "--Separate caller asks %d" % i
        ticket = server.schedule(str(i))
        print "--Separate caller got %s" % ticket.claim()


if __name__ == '__main__':
    server = Server()
    server.startServer()
    Thread(target=separateCaller, args=(server,)).start()

    result1 = server.schedule("1")
    print "result1: %s" % result1.claim()
    result2 = server.schedule("2")
    print "result2: %s" % result2.claim()
    result3 = server.schedule("3")
    print "result3: %s" % result3.claim()
    server.scheduleShutdown()
#############################################################


Play around with this a bit and see if it makes sense to you.  You might
also be interested in the amusing article "Starbucks Does Not Use
Two-Phase Commit":

    http://www.eaipatterns.com/ramblings/18_starbucks.html

as it touches on concepts in the code.


If you have more questions, please feel free to ask!



More information about the Tutor mailing list