Mercurial > public > sg101
comparison gpp/core/whos_online.py @ 423:3fe60148f75c
Fixing #203; use Redis for who's online function.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 23 Apr 2011 19:19:38 +0000 |
parents | |
children | 6f5fff924877 |
comparison
equal
deleted
inserted
replaced
422:6309814cd6f7 | 423:3fe60148f75c |
---|---|
1 """ | |
2 This module keeps track of who is online. We maintain records for both | |
3 authenticated users ("users") and non-authenticated visitors ("visitors"). | |
4 """ | |
5 import logging | |
6 | |
7 from django.conf import settings | |
8 import redis | |
9 | |
10 # Users and visitors each have 2 sets that we store in a Redis database: | |
11 # a current set and an old set. Whenever a user or visitor is seen, the | |
12 # current set is updated. At some interval, the current set is renamed | |
13 # to the old set, thus destroying it. At any given time, the union of the | |
14 # current and old sets is the "who's online" set. | |
15 | |
16 # Redis connection and database settings | |
17 | |
18 HOST = getattr(settings, 'WHOS_ONLINE_REDIS_HOST', 'localhost') | |
19 PORT = getattr(settings, 'WHOS_ONLINE_REDIS_PORT', 6379) | |
20 DB = getattr(settings, 'WHOS_ONLINE_REDIS_DB', 0) | |
21 | |
22 # Redis key names: | |
23 USER_CURRENT_KEY = "wo_user_current" | |
24 USER_OLD_KEY = "wo_user_old" | |
25 USER_KEYS = [USER_CURRENT_KEY, USER_OLD_KEY] | |
26 | |
27 VISITOR_CURRENT_KEY = "wo_visitor_current" | |
28 VISITOR_OLD_KEY = "wo_visitor_old" | |
29 VISITOR_KEYS = [VISITOR_CURRENT_KEY, VISITOR_OLD_KEY] | |
30 | |
31 | |
32 # Logging: we don't want a Redis malfunction to bring down the site. So we | |
33 # catch all Redis exceptions, log them, and press on. | |
34 logger = logging.getLogger(__name__) | |
35 | |
36 | |
37 def _get_connection(): | |
38 """ | |
39 Create and return a Redis connection. Returns None on failure. | |
40 """ | |
41 try: | |
42 conn = redis.Redis(host=HOST, port=PORT, db=DB) | |
43 return conn | |
44 except redis.RedisError, e: | |
45 logger.error(e) | |
46 | |
47 return None | |
48 | |
49 | |
50 def report_user(username): | |
51 """ | |
52 Call this function when a user has been seen. The username will be added to | |
53 the current set. | |
54 """ | |
55 conn = _get_connection() | |
56 if conn: | |
57 try: | |
58 conn.sadd(USER_CURRENT_KEY, username) | |
59 except redis.RedisError, e: | |
60 logger.error(e) | |
61 | |
62 | |
63 def report_visitor(ip): | |
64 """ | |
65 Call this function when a visitor has been seen. The IP address will be | |
66 added the current set. | |
67 """ | |
68 conn = _get_connection() | |
69 if conn: | |
70 try: | |
71 conn.sadd(VISITOR_CURRENT_KEY, ip) | |
72 except redis.RedisError, e: | |
73 logger.error(e) | |
74 | |
75 | |
76 def get_users_online(): | |
77 """ | |
78 Returns a set of user names which is the union of the current and old | |
79 sets. | |
80 """ | |
81 conn = _get_connection() | |
82 if conn: | |
83 try: | |
84 # Note that keys that do not exist are considered empty sets | |
85 return conn.sunion(USER_KEYS) | |
86 except redis.RedisError, e: | |
87 logger.error(e) | |
88 | |
89 return set() | |
90 | |
91 | |
92 def get_visitors_online(): | |
93 """ | |
94 Returns a set of visitor IP addresses which is the union of the current | |
95 and old sets. | |
96 """ | |
97 conn = _get_connection() | |
98 if conn: | |
99 try: | |
100 # Note that keys that do not exist are considered empty sets | |
101 return conn.sunion(VISITOR_KEYS) | |
102 except redis.RedisError, e: | |
103 logger.error(e) | |
104 | |
105 return set() | |
106 | |
107 | |
108 def _tick_set(conn, current, old): | |
109 """ | |
110 This function renames the set named "current" to "old". | |
111 """ | |
112 # An exception may be raised if the current key doesn't exist; if that | |
113 # happens we have to delete the old set because no one is online. | |
114 try: | |
115 conn.rename(current, old) | |
116 except redis.ResponseError: | |
117 try: | |
118 del conn[old] | |
119 except redis.RedisError, e: | |
120 logger.error(e) | |
121 except redis.RedisError, e: | |
122 logger.error(e) | |
123 | |
124 | |
125 def tick(): | |
126 """ | |
127 Call this function to "age out" the old sets by renaming the current sets | |
128 to the old. | |
129 """ | |
130 conn = _get_connection() | |
131 if conn: | |
132 _tick_set(conn, USER_CURRENT_KEY, USER_OLD_KEY) | |
133 _tick_set(conn, VISITOR_CURRENT_KEY, VISITOR_OLD_KEY) |