e-Book - Redis in Action

This book covers the use of Redis, an in-memory database/data structure server.
  • Foreword
  • Preface
  • Acknowledgments
  • About this Book
  • About the Cover Illustration
  • Part 1: Getting Started
  • Part 2: Core concepts
  • Part 3: Next steps
  • Appendix A
  • Appendix B
  • Buy the paperback

    4.4.2 Listing items in the marketplace

    In the process of listing, we’ll use a Redis operation called WATCH, which we combine
    with MULTI and EXEC, and sometimes UNWATCH or DISCARD. When we’ve watched keys
    with WATCH, if at any time some other client replaces, updates, or deletes any keys that
    we’ve WATCHed before we have performed the EXEC operation, our operations against
    Redis will fail with an error message when we try to EXEC (at which point we can retry
    or abort the operation). By using WATCH, MULTI/EXEC, and UNWATCH/DISCARD, we can
    ensure that the data that we’re working with doesn’t change while we’re doing something
    important, which protects us from data corruption.

    WHAT IS DISCARD?In the same way that UNWATCH will let us reset our connection
    if sent after WATCH but before MULTI, DISCARD will also reset the connection
    if sent after MULTI but before EXEC. That is to say, if we’d WATCHed a key or
    keys, fetched some data, and then started a transaction with MULTI followed
    by a group of commands, we could cancel the WATCH and clear out any
    queued commands with DISCARD. We don’t use DISCARD here, primarily
    because we know whether we want to perform a MULTI/EXEC or UNWATCH, so a
    DISCARD is unnecessary for our purposes.

    Let’s go about listing an item in the marketplace. To do so, we add the item to the
    market ZSET, while WATCHing the seller’s inventory to make sure that the item is still
    available to be sold. The function to list an item is shown here.

    Listing 4.5The list_item() function
    def list_item(conn, itemid, sellerid, price):
       inventory = "inventory:%s"%sellerid
       item = "%s.%s"%(itemid, sellerid)
       end = time.time() + 5
       pipe = conn.pipeline()

       while time.time() < end:

    Watch for changes to the user’s inventory.

             if not pipe.sismember(inventory, itemid):

    Verify that the user still has the item to be listed.


    If the item isn’t in the user’s inventory, stop watching the inventory key and return.

                return None
             pipe.zadd("market:", item, price)
             pipe.srem(inventory, itemid)

    Actually list the item.


    If execute returns without a WatchError being raised, then the transaction is complete and the inventory key is no longer watched.

             return True

          except redis.exceptions.WatchError:

    The user’s inventory was changed; retry.

       return False

    After some initial setup, we’ll do what we described earlier. We’ll tell Redis that we
    want to watch the seller’s inventory, verify that the seller can still sell the item, and if
    so, add the item to the market and remove the item from their invsentory. If there’s an update or change to the inventory while we’re looking at it, we’ll receive an error and
    retry, as is shown by the while loop outside of our actual operation.

    Let’s look at the sequence of operations that are performed when Frank (user 17) wants to sell ItemM for 97 e-dollars in figure 4.4.

    Figure 4.4list_item(conn, "ItemM", 17, 97)

    Generally, listing an item should occur without any significant issue, since only the
    user should be selling their own items (which is enforced farther up the application
    stack). But as I mentioned before, if a user’s inventory were to change between the
    WATCH and EXEC, our attempt to list the item would fail, and we’d retry.

    Now that you know how to list an item, it’s time to purchase an item.