Merge "Add support for PHP7 random_bytes in favor of mcrypt_create_iv"
[lhc/web/wiklou.git] / includes / deferred / WANCacheReapUpdate.php
1 <?php
2
3 use Psr\Log\LoggerInterface;
4
5 /**
6 * Class for fixing stale WANObjectCache keys using a purge event source
7 *
8 * This is useful for expiring keys that missed fire-and-forget purges. This uses the
9 * recentchanges table as a reliable stream to make certain keys reach consistency
10 * as soon as the underlying replica database catches up. These means that critical
11 * keys will not escape getting purged simply due to brief hiccups in the network,
12 * which are more prone to happen accross datacenters.
13 *
14 * ----
15 * "I was trying to cheat death. I was only trying to surmount for a little while the
16 * darkness that all my life I surely knew was going to come rolling in on me some day
17 * and obliterate me. I was only to stay alive a little brief while longer, after I was
18 * already gone. To stay in the light, to be with the living, a little while past my time."
19 * -- Notes for "Blues of a Lifetime", by [[Cornell Woolrich]]
20 *
21 * @since 1.28
22 */
23 class WANCacheReapUpdate implements DeferrableUpdate {
24 /** @var IDatabase */
25 private $db;
26 /** @var LoggerInterface */
27 private $logger;
28
29 /**
30 * @param IDatabase $db
31 * @param LoggerInterface $logger
32 */
33 public function __construct( IDatabase $db, LoggerInterface $logger ) {
34 $this->db = $db;
35 $this->logger = $logger;
36 }
37
38 function doUpdate() {
39 $reaper = new WANObjectCacheReaper(
40 ObjectCache::getMainWANInstance(),
41 ObjectCache::getLocalClusterInstance(),
42 [ $this, 'getTitleChangeEvents' ],
43 [ $this, 'getEventAffectedKeys' ],
44 [
45 'channel' => 'table:recentchanges:' . $this->db->getWikiID(),
46 'logger' => $this->logger
47 ]
48 );
49
50 $reaper->invoke( 100 );
51 }
52
53 /**
54 * @see WANObjectCacheRepear
55 *
56 * @param int $start
57 * @param int $id
58 * @param int $end
59 * @param int $limit
60 * @return TitleValue[]
61 */
62 public function getTitleChangeEvents( $start, $id, $end, $limit ) {
63 $db = $this->db;
64 $encStart = $db->addQuotes( $db->timestamp( $start ) );
65 $encEnd = $db->addQuotes( $db->timestamp( $end ) );
66 $id = (int)$id; // cast NULL => 0 since rc_id is an integer
67
68 $res = $db->select(
69 'recentchanges',
70 [ 'rc_namespace', 'rc_title', 'rc_timestamp', 'rc_id' ],
71 [
72 $db->makeList( [
73 "rc_timestamp > $encStart",
74 "rc_timestamp = $encStart AND rc_id > " . $db->addQuotes( $id )
75 ], LIST_OR ),
76 "rc_timestamp < $encEnd"
77 ],
78 __METHOD__,
79 [ 'ORDER BY' => 'rc_timestamp ASC, rc_id ASC', 'LIMIT' => $limit ]
80 );
81
82 $events = [];
83 foreach ( $res as $row ) {
84 $events[] = [
85 'id' => (int)$row->rc_id,
86 'pos' => (int)wfTimestamp( TS_UNIX, $row->rc_timestamp ),
87 'item' => new TitleValue( (int)$row->rc_namespace, $row->rc_title )
88 ];
89 }
90
91 return $events;
92 }
93
94 /**
95 * Gets a list of important cache keys associated with a title
96 *
97 * @see WANObjectCacheRepear
98 * @param WANObjectCache $cache
99 * @param TitleValue $t
100 * @returns string[]
101 */
102 public function getEventAffectedKeys( WANObjectCache $cache, TitleValue $t ) {
103 /** @var WikiPage[]|LocalFile[]|User[] $entities */
104 $entities = [];
105
106 $entities[] = WikiPage::factory( Title::newFromTitleValue( $t ) );
107 if ( $t->inNamespace( NS_FILE ) ) {
108 $entities[] = wfLocalFile( $t->getText() );
109 }
110 if ( $t->inNamespace( NS_USER ) ) {
111 $entities[] = User::newFromName( $t->getText(), false );
112 }
113
114 $keys = [];
115 foreach ( $entities as $entity ) {
116 if ( $entity ) {
117 $keys = array_merge( $keys, $entity->getMutableCacheKeys( $cache ) );
118 }
119 }
120 if ( $keys ) {
121 $this->logger->debug( __CLASS__ . ': got key(s) ' . implode( ', ', $keys ) );
122 }
123
124 return $keys;
125 }
126 }