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

    8.1.1 User information

    In a variety of online services and social networks, user objects can be the basic building blocks from which everything else is derived. Our Twitter work-alike is no different.

    Figure 8.1Example user information stored in a HASH

    We’ll store user information inside of Redis as a HASH, similar to how we stored articles in chapter 1. Data that we’ll store includes the username of the user, how many followers they have, how many people they’re following, how many status messages they’ve posted, their sign-up date, and any other meta-information we decide to store down the line. A sample
    HASH that includes this information for a user with the username of dr_josiah (my Twitter username) is shown in figure 8.1.

    From this figure, you can see that I have a modest number of followers, along with other information. When a new user signs up, we only need to create an object with the following, followers, and post count set to zero, a new timestamp for the sign-up time, and the relevant username. The function to perform this initial creation is shown next.

    Listing 8.1How to create a new user profile HASH
    def create_user(conn, login, name):
        llogin = login.lower()
        lock = acquire_lock_with_timeout(conn, 'user:' + llogin, 1)

    Try to acquire the lock for the lowercased version of the login name. This function is defined in chapter 6.

        if not lock:
            return None

    If we couldn’t get the lock, then someone else already has the same login name.

        if conn.hget('users:', llogin):
            return None

    We also store a HASH of lowercased login names to user IDs, so if there’s already a login name that maps to an ID, we know and won’t give it to a second person.

        id = conn.incr('user:id:')

    Each user is given a unique ID, generated by incrementing a counter.

        pipeline = conn.pipeline(True)
        pipeline.hset('users:', llogin, id)

    Add the lowercased login name to the HASH that maps from login names to user IDs.

        pipeline.hmset('user:%s'%id, {
            'login': login,
            'id': id,
            'name': name,
            'followers': 0,
            'following': 0,
            'posts': 0,
            'signup': time.time(),

    Add the user information to the user’s HASH.

        release_lock(conn, 'user:' + llogin, lock)

    Release the lock over the login name.

        return id

    Return the ID of the user.

    In our function, we perform the expected setting of the initial user information in the
    user’s HASH, but we also acquire a lock around the user’s login name. This lock is necessary:
    it guarantees that we won’t have two requests trying to create a user with the
    same login at the same time. After locking, we verify that the login name hasn’t been
    taken by another user. If the name hasn’t been taken, we generate a new unique ID for
    the user, add the login name to the mapping of login names to user IDs, and then create
    the user’s HASH.

    SENSITIVE USER INFORMATIONBecause the user HASH will be fetched countless times for rendering a template, or for returning directly as a response to an API request, we don’t store sensitive user information in this HASH. For now, we’ll assume that HASHed passwords, email addresses, and more are stored at other keys, or in a different database entirely.

    We’ve finished creating the user and setting all of the necessary meta-information
    about them. From here, the next step in building our Twitter work-alike is the status
    message itself.