Documentation - Redise Pack

A guide to Redise Pack installation, operation and administration

open all | close all

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 WATCHMULTI/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 MULTIDISCARD 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.5 The 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:
      try:
         pipe.watch(inventory)

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.

            pipe.unwatch()

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

            return None

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

Actually list the item.

         pipe.execute()

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:
         pass

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 inventory. 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.4 list_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.