pierre@0
|
1 <?php |
pierre@0
|
2 // $Id: ad_cache_memcache.inc,v 1.1.2.6.2.6 2009/02/16 23:12:28 jeremy Exp $ |
pierre@0
|
3 |
pierre@0
|
4 /** |
pierre@0
|
5 * @file |
pierre@0
|
6 * Memcache include. |
pierre@0
|
7 * |
pierre@0
|
8 * Copyright (c) 2005-2009. |
pierre@0
|
9 * Jeremy Andrews <jeremy@tag1consulting.com>. |
pierre@0
|
10 */ |
pierre@0
|
11 |
pierre@0
|
12 /** |
pierre@0
|
13 * TODO: Debug Raw and IFrame display methods, neither currently seem to work |
pierre@0
|
14 * with this cache type. |
pierre@0
|
15 */ |
pierre@0
|
16 |
pierre@0
|
17 /** |
pierre@0
|
18 * Called by adserve.inc, display an ad from memcache. |
pierre@0
|
19 */ |
pierre@0
|
20 function ad_cache_memcache() { |
pierre@0
|
21 _debug_echo('Memcache: entering ad_cache_memcache().'); |
pierre@0
|
22 |
pierre@0
|
23 // TODO: Move the meat of this function into adserve.php, simplifying what |
pierre@0
|
24 // cache plugins have to do and removing duplicated logic. |
pierre@0
|
25 $init_cache = array(); |
pierre@0
|
26 $init_func = ad_cache_memcache_hook($init_cache, 'include_file_init', 'include_func_init'); |
pierre@0
|
27 |
pierre@0
|
28 $hostid = adserve_variable('hostid') ? adserve_variable('hostid') : 'none'; |
pierre@0
|
29 if ($hostid == 'none' || ad_memcache_get("ad-hostid-$hostid")) { |
pierre@0
|
30 if (function_exists($init_func)) { |
pierre@0
|
31 $init = $init_func($init_cache, $hostid); |
pierre@0
|
32 } |
pierre@0
|
33 if (!empty($init)) { |
pierre@0
|
34 if (adserve_variable('debug')) { |
pierre@0
|
35 echo "Memcache: initialized externally:<pre>\n"; |
pierre@0
|
36 print_r($init); |
pierre@0
|
37 echo '</pre>'; |
pierre@0
|
38 } |
pierre@0
|
39 $type = $init['type']; |
pierre@0
|
40 $id = $init['id']; |
pierre@0
|
41 $group = $init['group']; |
pierre@0
|
42 $aids = explode(',', $id); |
pierre@0
|
43 adserve_variable('quantity', $init['quantity']); |
pierre@0
|
44 } |
pierre@0
|
45 else { |
pierre@0
|
46 if ($id = adserve_variable('nids')) { |
pierre@0
|
47 $type = 'node'; |
pierre@0
|
48 } |
pierre@0
|
49 else if ($id = adserve_variable('tids')) { |
pierre@0
|
50 $type = 'taxonomy'; |
pierre@0
|
51 } |
pierre@0
|
52 else { |
pierre@0
|
53 $type = 'default'; |
pierre@0
|
54 $id = 0; |
pierre@0
|
55 } |
pierre@0
|
56 $aids = ad_cache_memcache_get_ids($type, $id); |
pierre@0
|
57 $group = $id; |
pierre@0
|
58 } |
pierre@0
|
59 adserve_variable('group', $group); |
pierre@0
|
60 |
pierre@0
|
61 if (adserve_variable('debug')) { |
pierre@0
|
62 echo 'Memcache: selecting from the following ad id(s): '; |
pierre@0
|
63 if (empty($aids)) { |
pierre@0
|
64 echo 'none.<br />'; |
pierre@0
|
65 } |
pierre@0
|
66 else { |
pierre@0
|
67 echo implode(', ', $aids) .'.<br />'; |
pierre@0
|
68 } |
pierre@0
|
69 } |
pierre@0
|
70 |
pierre@0
|
71 $ids = adserve_variable("$type-ids"); |
pierre@0
|
72 if ($ids == NULL) { |
pierre@0
|
73 $ids = array(); |
pierre@0
|
74 } |
pierre@0
|
75 |
pierre@0
|
76 $output = ''; |
pierre@0
|
77 $selected = adserve_select_ad($aids, adserve_variable('quantity'), $ids); |
pierre@0
|
78 adserve_variable("$type-ids", array_merge($selected, $ids)); |
pierre@0
|
79 foreach ($selected as $aid) { |
pierre@0
|
80 if ($aid = (int)$aid) { |
pierre@0
|
81 $ad = ad_cache_memcache_get_ad($aid); |
pierre@0
|
82 |
pierre@0
|
83 if (!empty($output)) { |
pierre@0
|
84 $display_count++; |
pierre@0
|
85 $output .= "<div class=\"space\" id=\"$id-$displayed_count\"></div>"; |
pierre@0
|
86 } |
pierre@0
|
87 |
pierre@0
|
88 $output .= $ad->display; |
pierre@0
|
89 } |
pierre@0
|
90 else { |
pierre@0
|
91 $ad = array(); |
pierre@0
|
92 } |
pierre@0
|
93 |
pierre@0
|
94 _debug_echo("Displaying AID: $aid."); |
pierre@0
|
95 $action = $aid ? 'view' : 'count'; |
pierre@0
|
96 ad_cache_memcache_increment($action, $aid, $group, $hostid, $ad); |
pierre@0
|
97 } |
pierre@0
|
98 if (empty($output)) { |
pierre@0
|
99 adserve_variable('error', TRUE); |
pierre@0
|
100 $output = 'No active ads were found in the '. (empty($nids) ? 'tids' : 'nids') ." '$id'."; |
pierre@0
|
101 _debug_echo("Memcache: {$output}"); |
pierre@0
|
102 } |
pierre@0
|
103 } |
pierre@0
|
104 else { |
pierre@0
|
105 _debug_echo("Memcache: invalid hostid: '$hostid'."); |
pierre@0
|
106 $output = 'You do not have permission to display ads.'; |
pierre@0
|
107 } |
pierre@0
|
108 |
pierre@0
|
109 return $output; |
pierre@0
|
110 } |
pierre@0
|
111 |
pierre@0
|
112 function ad_cache_memcache_hook(&$cache, $hook, $func) { |
pierre@0
|
113 |
pierre@0
|
114 if (empty($cache)) { |
pierre@0
|
115 _debug_echo('Memcache: retrieving hook info from cache.'); |
pierre@0
|
116 $cache = ad_memcache_get('ad-cache-hook'); |
pierre@0
|
117 } |
pierre@0
|
118 $include_func = NULL; |
pierre@0
|
119 if (is_array($cache) && !empty($cache)) { |
pierre@0
|
120 $include_file = adserve_variable('root_dir') .'/'. $cache[$hook]; |
pierre@0
|
121 if (file_exists($include_file) && is_file($include_file)) { |
pierre@0
|
122 _debug_echo("Memcache: including external file: '$include_file'."); |
pierre@0
|
123 include_once($include_file); |
pierre@0
|
124 } |
pierre@0
|
125 else if (is_file($include_file)) { |
pierre@0
|
126 _debug_echo("Memcache: unable to find external file: '$include_file'."); |
pierre@0
|
127 } |
pierre@0
|
128 else { |
pierre@0
|
129 _debug_echo('Memcache: no include file defined in cache.'); |
pierre@0
|
130 } |
pierre@0
|
131 $include_func = $cache[$func]; |
pierre@0
|
132 if ($include_func) { |
pierre@0
|
133 _debug_echo("Memcache: returning requested func($func): '$include_func'."); |
pierre@0
|
134 } |
pierre@0
|
135 } |
pierre@0
|
136 return ($include_func); |
pierre@0
|
137 } |
pierre@0
|
138 |
pierre@0
|
139 function ad_cache_memcache_get_ids($op = 'default', $id = 0) { |
pierre@0
|
140 switch ($op) { |
pierre@0
|
141 |
pierre@0
|
142 case 'node': { |
pierre@0
|
143 $ids = explode(',', $id); |
pierre@0
|
144 break; |
pierre@0
|
145 } |
pierre@0
|
146 |
pierre@0
|
147 case 'taxonomy': { |
pierre@0
|
148 $ids = ad_memcache_get("ad-taxonomy-cache-$id"); |
pierre@0
|
149 if (!$ids || empty($ids)) { |
pierre@0
|
150 $taxonomy = ad_memcache_get('ad-taxonomy'); |
pierre@0
|
151 $cache = array(); |
pierre@0
|
152 $ids = explode(',', $id); |
pierre@0
|
153 foreach ($ids as $tid) { |
pierre@0
|
154 if (is_array($taxonomy[$tid])) { |
pierre@0
|
155 $cache += $taxonomy[$tid]; |
pierre@0
|
156 } |
pierre@0
|
157 } |
pierre@0
|
158 // Rebuild keys from 0, cache for quick re-use on next ad display. |
pierre@0
|
159 $ids = array_values($cache); |
pierre@0
|
160 ad_memcache_set("ad-taxonomy-cache-$id", $ids); |
pierre@0
|
161 } |
pierre@0
|
162 break; |
pierre@0
|
163 } |
pierre@0
|
164 |
pierre@0
|
165 default: { |
pierre@0
|
166 $taxonomy = ad_memcache_get('ad-taxonomy'); |
pierre@0
|
167 $ids = $taxonomy[0]; |
pierre@0
|
168 break; |
pierre@0
|
169 } |
pierre@0
|
170 |
pierre@0
|
171 } |
pierre@0
|
172 |
pierre@0
|
173 return $ids; |
pierre@0
|
174 } |
pierre@0
|
175 |
pierre@0
|
176 function ad_cache_memcache_get_ad($aid) { |
pierre@0
|
177 static $load = FALSE; |
pierre@0
|
178 |
pierre@0
|
179 $ad = ad_memcache_get("ad-aid-$aid"); |
pierre@0
|
180 |
pierre@0
|
181 if (!$load && !is_object($ad)) { |
pierre@0
|
182 $load = TRUE; |
pierre@0
|
183 adserve_bootstrap(); |
pierre@0
|
184 $ad_memcache_build = variable_get('ad_memcache_build', ''); |
pierre@0
|
185 if ((time() - $ad_memcache_build) >= 60) { |
pierre@0
|
186 ad_cache_memcache_build(); |
pierre@0
|
187 } |
pierre@0
|
188 } |
pierre@0
|
189 |
pierre@0
|
190 return $ad; |
pierre@0
|
191 } |
pierre@0
|
192 |
pierre@0
|
193 /** |
pierre@0
|
194 * Increment impressions counter in memcache. |
pierre@0
|
195 */ |
pierre@0
|
196 function ad_cache_memcache_increment($action, $aid, $group = '', $hostid = '', $ad = array()) { |
pierre@0
|
197 static $timestamp = NULL; |
pierre@0
|
198 |
pierre@0
|
199 _debug_echo("Memcache: increment action($action) aid($aid) group($group) hostid($hostid)."); |
pierre@0
|
200 |
pierre@0
|
201 if ($aid && !is_object($ad)) { |
pierre@0
|
202 _debug_echo("Invalid ad id: $aid."); |
pierre@0
|
203 return (0); |
pierre@0
|
204 } |
pierre@0
|
205 |
pierre@0
|
206 if (!isset($timestamp)) { |
pierre@0
|
207 $timestamp = date('YmdH'); |
pierre@0
|
208 } |
pierre@0
|
209 $counters = ad_memcache_get("ad-counters-$aid"); |
pierre@0
|
210 |
pierre@0
|
211 $update = TRUE; |
pierre@0
|
212 if (!is_array($counters) || !isset($counters["$action:$group:$hostid:$timestamp"])) { |
pierre@0
|
213 _debug_echo("Memcache: adding map: action($action) aid($aid) group($group) hostid($hostid) timestamp($timestamp)"); |
pierre@0
|
214 ad_memcache_increment_map($action, $aid, $group, $hostid, $timestamp); |
pierre@0
|
215 } |
pierre@0
|
216 |
pierre@0
|
217 $rc = ad_memcache_increment("ad-$action-$aid-$group-$hostid-$timestamp"); |
pierre@0
|
218 _debug_echo("Memcache: incrementing ad-$action-$aid-$group-$hostid-$timestamp ($rc)"); |
pierre@0
|
219 } |
pierre@0
|
220 |
pierre@0
|
221 /** |
pierre@0
|
222 * The maximum time any process can hold a given lock, in seconds. |
pierre@0
|
223 */ |
pierre@0
|
224 define('AD_MEMCACHE_LOCK_LIMIT', 2); |
pierre@0
|
225 |
pierre@0
|
226 /** |
pierre@0
|
227 * Store a value in memcache. |
pierre@0
|
228 */ |
pierre@0
|
229 function ad_memcache_set($key, $value, $timeout = 86400) { |
pierre@0
|
230 $memcache = ad_memcache_init(); |
pierre@0
|
231 |
pierre@0
|
232 return $memcache->set($key, $value, MEMCACHE_COMPRESSED, $timeout); |
pierre@0
|
233 } |
pierre@0
|
234 |
pierre@0
|
235 /** |
pierre@0
|
236 * Store a value in memcache. |
pierre@0
|
237 */ |
pierre@0
|
238 function ad_memcache_add($key, $value, $timeout = 86400) { |
pierre@0
|
239 $memcache = ad_memcache_init(); |
pierre@0
|
240 |
pierre@0
|
241 return $memcache->add($key, $value, MEMCACHE_COMPRESSED, $timeout); |
pierre@0
|
242 } |
pierre@0
|
243 |
pierre@0
|
244 /** |
pierre@0
|
245 * Get a value from memcache. |
pierre@0
|
246 */ |
pierre@0
|
247 function ad_memcache_get($key) { |
pierre@0
|
248 $memcache = ad_memcache_init(); |
pierre@0
|
249 |
pierre@0
|
250 return $memcache->get($key); |
pierre@0
|
251 } |
pierre@0
|
252 |
pierre@0
|
253 /** |
pierre@0
|
254 * Delete a value from memcache. |
pierre@0
|
255 */ |
pierre@0
|
256 function ad_memcache_delete($key) { |
pierre@0
|
257 $memcache = ad_memcache_init(); |
pierre@0
|
258 |
pierre@0
|
259 return $memcache->delete($key); |
pierre@0
|
260 } |
pierre@0
|
261 |
pierre@0
|
262 /** |
pierre@0
|
263 * Get a lock in memcache. |
pierre@0
|
264 */ |
pierre@0
|
265 function ad_memcache_lock($key, $wait = TRUE) { |
pierre@0
|
266 $loop = 0; |
pierre@0
|
267 $lock = FALSE; |
pierre@0
|
268 while ($lock == FALSE) { |
pierre@0
|
269 $lock = ad_memcache_add("LOCK-$key-LOCK", TRUE, AD_MEMCACHE_LOCK_LIMIT); |
pierre@0
|
270 if (!$lock && $wait) { |
pierre@0
|
271 if ($loop++ > 50) { |
pierre@0
|
272 // Hard limit of 5 seconds, after which we fail to grab a lock. |
pierre@0
|
273 return FALSE; |
pierre@0
|
274 } |
pierre@0
|
275 // Wait 1/10th of a second and try again. |
pierre@0
|
276 usleep(100000); |
pierre@0
|
277 } |
pierre@0
|
278 else if (!$lock && !$wait) { |
pierre@0
|
279 return FALSE; |
pierre@0
|
280 } |
pierre@0
|
281 } |
pierre@0
|
282 return TRUE; |
pierre@0
|
283 } |
pierre@0
|
284 |
pierre@0
|
285 /** |
pierre@0
|
286 * Release a lock in memcache. |
pierre@0
|
287 */ |
pierre@0
|
288 function ad_memcache_unlock($key) { |
pierre@0
|
289 ad_memcache_delete("LOCK-$key-LOCK"); |
pierre@0
|
290 } |
pierre@0
|
291 |
pierre@0
|
292 /** |
pierre@0
|
293 * Increment a numerical value in memcache. |
pierre@0
|
294 */ |
pierre@0
|
295 function ad_memcache_increment($key, $value = 1) { |
pierre@0
|
296 $memcache = ad_memcache_init(); |
pierre@0
|
297 |
pierre@0
|
298 $rc = $memcache->increment($key, $value); |
pierre@0
|
299 if ($rc === FALSE) { |
pierre@0
|
300 // We tried incrementing a counter that hasn't yet been initialized. |
pierre@0
|
301 $rc = $memcache->set($key, $value); |
pierre@0
|
302 if ($rc === FALSE) { |
pierre@0
|
303 // Another process already initialized the counter, increment it. |
pierre@0
|
304 $rc = $memcache->increment($key); |
pierre@0
|
305 } |
pierre@0
|
306 } |
pierre@0
|
307 return $rc; |
pierre@0
|
308 } |
pierre@0
|
309 |
pierre@0
|
310 /** |
pierre@0
|
311 * Decrement a numerical value in memcache. |
pierre@0
|
312 */ |
pierre@0
|
313 function ad_memcache_decrement($key, $value = 1) { |
pierre@0
|
314 $memcache = ad_memcache_init(); |
pierre@0
|
315 |
pierre@0
|
316 $rc = $memcache->decrement($key, $value); |
pierre@0
|
317 if ($rc === FALSE) { |
pierre@0
|
318 // We tried incrementing a counter that hasn't yet been initialized. |
pierre@0
|
319 $rc = $memcache->set($key, $value); |
pierre@0
|
320 if ($rc === FALSE) { |
pierre@0
|
321 // Another process already initialized the counter, increment it. |
pierre@0
|
322 $rc = $memcache->decrement($key); |
pierre@0
|
323 } |
pierre@0
|
324 } |
pierre@0
|
325 return $rc; |
pierre@0
|
326 } |
pierre@0
|
327 |
pierre@0
|
328 /** |
pierre@0
|
329 * Update mapping which allows us to quickly find stats in memcache when |
pierre@0
|
330 * feeding them into the database. |
pierre@0
|
331 */ |
pierre@0
|
332 function ad_memcache_increment_map($action, $aid, $group, $hostid, $timestamp) { |
pierre@0
|
333 $key = "ad-counters-$aid"; |
pierre@0
|
334 if (ad_memcache_lock($key)) { |
pierre@0
|
335 $counters = ad_memcache_get($key); |
pierre@0
|
336 if (!is_array($counters) || !isset($counters["$action:$group:$hostid:$timestamp"])) { |
pierre@0
|
337 $counters["$action:$group:$hostid:$timestamp"] = "$action:$group:$hostid:$timestamp"; |
pierre@0
|
338 ad_memcache_set($key, $counters); |
pierre@0
|
339 } |
pierre@0
|
340 ad_memcache_unlock($key); |
pierre@0
|
341 } |
pierre@0
|
342 } |
pierre@0
|
343 |
pierre@0
|
344 /** |
pierre@0
|
345 * Decrement a numerical value in memcache. |
pierre@0
|
346 * TODO: Use the same configuration style as Drupal's memcache module, |
pierre@0
|
347 * supporting multiple memcache servers, etc. |
pierre@0
|
348 */ |
pierre@0
|
349 function ad_memcache_init() { |
pierre@0
|
350 static $memcache = NULL; |
pierre@0
|
351 |
pierre@0
|
352 if (!$memcache) { |
pierre@0
|
353 $memcache = new Memcache; |
pierre@0
|
354 $memcache->addServer('localhost', 11211); |
pierre@0
|
355 } |
pierre@0
|
356 return $memcache; |
pierre@0
|
357 } |
pierre@0
|
358 |
pierre@0
|
359 /** |
pierre@0
|
360 * Allow external ad selection logic. |
pierre@0
|
361 */ |
pierre@0
|
362 function ad_cache_memcache_adserve_select($ads, $invalid) { |
pierre@0
|
363 $cache = array(); |
pierre@0
|
364 if ($select_func = ad_cache_memcache_hook($cache, 'include_file_select', 'include_func_select')) { |
pierre@0
|
365 _debug_echo("Memcache: adserve_select: invoking '$select_func()'"); |
pierre@0
|
366 if (function_exists($select_func)) { |
pierre@0
|
367 if (is_array($cache) && !empty($cache)) { |
pierre@0
|
368 return $select_func($ads, $invalid, $cache); |
pierre@0
|
369 } |
pierre@0
|
370 else { |
pierre@0
|
371 _debug_echo("Memcache: unexpected error: cache empty."); |
pierre@0
|
372 } |
pierre@0
|
373 } |
pierre@0
|
374 else { |
pierre@0
|
375 _debug_echo("Memcache: adserve_select: '$include_func_select()' not found"); |
pierre@0
|
376 } |
pierre@0
|
377 } |
pierre@0
|
378 else { |
pierre@0
|
379 _debug_echo("Memcache: adserve_select: no select function defined"); |
pierre@0
|
380 } |
pierre@0
|
381 } |
pierre@0
|
382 |
pierre@0
|
383 /** |
pierre@0
|
384 * Allow external exit text. |
pierre@0
|
385 */ |
pierre@0
|
386 function ad_cache_memcache_adserve_exit_text() { |
pierre@0
|
387 $cache = array(); |
pierre@0
|
388 if ($exit_text_func = ad_cache_memcache_hook($cache, 'include_file_exit_text', 'include_func_exit_text')) { |
pierre@0
|
389 _debug_echo("Memcache: adserve_exit_text: invoking '$exit_text_func()'"); |
pierre@0
|
390 if (function_exists($exit_text_func)) { |
pierre@0
|
391 return $exit_text_func(); |
pierre@0
|
392 } |
pierre@0
|
393 else { |
pierre@0
|
394 _debug_echo("Memcache: adserve_exit_text: '$exit_text_func()' not found"); |
pierre@0
|
395 } |
pierre@0
|
396 } |
pierre@0
|
397 else { |
pierre@0
|
398 _debug_echo("Memcache: adserve_exit_text: no exit_text function defined"); |
pierre@0
|
399 } |
pierre@0
|
400 } |
pierre@0
|
401 |