dot Staying sharp with your dev skills is a must.

Check out what's new in Redis University.

8.1.1 User information

back to home

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.