8.1.1 User information

  • Redis in Action – Home
  • Foreword
  • Preface
  • Part 1: Getting Started
  • 1.1.1 Redis compared to other databases and software
  • 1.1.3 Why Redis?
  • 1.3.2 Posting and fetching articles
  • 1.3.3 Grouping articles
  • 2.1 Login and cookie caching
  • 2.2 Shopping carts in Redis
  • 2.3 Web page caching
  • 2.5 Web page analytics
  • 3.1 Strings
  • 3.2 Lists
  • 3.4 Hashes
  • 3.5 Sorted sets
  • 3.7.1 Sorting
  • 3.7.2 Basic Redis transactions
  • 3.7.3 Expiring keys
  • 4.4 Redis transactions
  • 4.5 Non-transactional pipelines
  • 4.7 Summary
  • 5.1 Logging to Redis
  • 5.2 Counters and statistics
  • 5.2.2 Storing statistics in Redis
  • 5.4.1 Using Redis to store configuration information
  • 5.4.2 One Redis server per application component
  • 5.4.3 Automatic Redis connection management
  • 6.1 Autocomplete
  • 6.2 Distributed locking
  • 6.3 Counting semaphores
  • 6.6 Distributing files with Redis
  • 6.2.1 Why locks are important
  • 6.2.2 Simple locks
  • 6.2.3 Building a lock in Redis
  • 6.2.5 Locks with timeouts
  • 6.3.2 Fair semaphores
  • 6.3.4 Preventing race conditions
  • 6.5.1 Single-recipient publish/subscribe replacement
  • 6.5.2 Multiple-recipient publish/subscribe replacement
  • 6.6.2 Sending files
  • 7.1 Searching in Redis
  • 7.2 Sorted Indexes
  • 7.5 Summary
  • 7.1.2 Sorting search results
  • 8.1.1 User information
  • 8.5.1 Data to be streamed
  • 9.2.2 SETs
  • 9.4 Summary
  • Chapter 11: Scripting Redis with Lua
  • 11.1.1 Loading Lua scripts into Redis
  • 11.2 Rewriting locks and semaphores with Lua
  • 11.5 Summary
  • 11.2.1 Why locks in Lua?
  • 11.2.2 Rewriting our lock
  • B.1 Forums for help
  • Buy the paperback
  • Redis in Action – Home
  • Foreword
  • Preface
  • Part 1: Getting Started
  • 1.1.1 Redis compared to other databases and software
  • 1.1.3 Why Redis?
  • 1.3.2 Posting and fetching articles
  • 1.3.3 Grouping articles
  • 2.1 Login and cookie caching
  • 2.2 Shopping carts in Redis
  • 2.3 Web page caching
  • 2.5 Web page analytics
  • 3.1 Strings
  • 3.2 Lists
  • 3.4 Hashes
  • 3.5 Sorted sets
  • 3.7.1 Sorting
  • 3.7.2 Basic Redis transactions
  • 3.7.3 Expiring keys
  • 4.4 Redis transactions
  • 4.5 Non-transactional pipelines
  • 4.7 Summary
  • 5.1 Logging to Redis
  • 5.2 Counters and statistics
  • 5.2.2 Storing statistics in Redis
  • 5.4.1 Using Redis to store configuration information
  • 5.4.2 One Redis server per application component
  • 5.4.3 Automatic Redis connection management
  • 6.1 Autocomplete
  • 6.2 Distributed locking
  • 6.3 Counting semaphores
  • 6.6 Distributing files with Redis
  • 6.2.1 Why locks are important
  • 6.2.2 Simple locks
  • 6.2.3 Building a lock in Redis
  • 6.2.5 Locks with timeouts
  • 6.3.2 Fair semaphores
  • 6.3.4 Preventing race conditions
  • 6.5.1 Single-recipient publish/subscribe replacement
  • 6.5.2 Multiple-recipient publish/subscribe replacement
  • 6.6.2 Sending files
  • 7.1 Searching in Redis
  • 7.2 Sorted Indexes
  • 7.5 Summary
  • 7.1.2 Sorting search results
  • 8.1.1 User information
  • 8.5.1 Data to be streamed
  • 9.2.2 SETs
  • 9.4 Summary
  • Chapter 11: Scripting Redis with Lua
  • 11.1.1 Loading Lua scripts into Redis
  • 11.2 Rewriting locks and semaphores with Lua
  • 11.5 Summary
  • 11.2.1 Why locks in Lua?
  • 11.2.2 Rewriting our lock
  • B.1 Forums for help
  • 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.1 Example 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.1 How 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.

        })
    
     
        pipeline.execute()
    
     
        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.