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