MediaWiki  master
SqlModuleDependencyStore.php
Go to the documentation of this file.
1 <?php
22 
23 use InvalidArgumentException;
28 
39  private $lb;
40 
44  public function __construct( ILoadBalancer $lb ) {
45  $this->lb = $lb;
46  }
47 
48  public function retrieveMulti( $type, array $entities ) {
49  try {
50  $dbr = $this->getReplicaDb();
51 
52  $depsBlobByEntity = $this->fetchDependencyBlobs( $entities, $dbr );
53 
54  $storedPathsByEntity = [];
55  foreach ( $depsBlobByEntity as $entity => $depsBlob ) {
56  $storedPathsByEntity[$entity] = json_decode( $depsBlob, true );
57  }
58 
59  $results = [];
60  foreach ( $entities as $entity ) {
61  $paths = $storedPathsByEntity[$entity] ?? [];
62  $results[$entity] = $this->newEntityDependencies( $paths, null );
63  }
64 
65  return $results;
66  } catch ( DBError $e ) {
67  throw new DependencyStoreException( $e->getMessage() );
68  }
69  }
70 
71  public function storeMulti( $type, array $dataByEntity, $ttl ) {
72  try {
73  $dbw = $this->getMasterDb();
74 
75  $depsBlobByEntity = $this->fetchDependencyBlobs( array_keys( $dataByEntity ), $dbw );
76 
77  $rows = [];
78  foreach ( $dataByEntity as $entity => $data ) {
79  list( $module, $variant ) = $this->getEntityNameComponents( $entity );
80  if ( !is_array( $data[self::KEY_PATHS] ) ) {
81  throw new InvalidArgumentException( "Invalid entry for '$entity'" );
82  }
83 
84  // Normalize the list by removing duplicates and sortings
85  $paths = array_values( array_unique( $data[self::KEY_PATHS] ) );
86  sort( $paths, SORT_STRING );
87  $blob = json_encode( $paths, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
88 
89  $existingBlob = $depsBlobByEntity[$entity] ?? null;
90  if ( $blob !== $existingBlob ) {
91  $rows[] = [
92  'md_module' => $module,
93  'md_skin' => $variant,
94  'md_deps' => $blob
95  ];
96  }
97  }
98 
99  // @TODO: use a single query with VALUES()/aliases support in DB wrapper
100  // See https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
101  foreach ( $rows as $row ) {
102  $dbw->upsert(
103  'module_deps',
104  $row,
105  [ [ 'md_module', 'md_skin' ] ],
106  [
107  'md_deps' => $row['md_deps'],
108  ],
109  __METHOD__
110  );
111  }
112  } catch ( DBError $e ) {
113  throw new DependencyStoreException( $e->getMessage() );
114  }
115  }
116 
117  public function remove( $type, $entities ) {
118  try {
119  $dbw = $this->getMasterDb();
120 
121  $disjunctionConds = [];
122  foreach ( (array)$entities as $entity ) {
123  list( $module, $variant ) = $this->getEntityNameComponents( $entity );
124  $disjunctionConds[] = $dbw->makeList(
125  [ 'md_skin' => $variant, 'md_module' => $module ],
127  );
128  }
129 
130  if ( $disjunctionConds ) {
131  $dbw->delete(
132  'module_deps',
133  $dbw->makeList( $disjunctionConds, $dbw::LIST_OR ),
134  __METHOD__
135  );
136  }
137  } catch ( DBError $e ) {
138  throw new DependencyStoreException( $e->getMessage() );
139  }
140  }
141 
142  public function renew( $type, $entities, $ttl ) {
143  // no-op
144  }
145 
151  private function fetchDependencyBlobs( array $entities, IDatabase $db ) {
152  $modulesByVariant = [];
153  foreach ( $entities as $entity ) {
154  list( $module, $variant ) = $this->getEntityNameComponents( $entity );
155  $modulesByVariant[$variant][] = $module;
156  }
157 
158  $disjunctionConds = [];
159  foreach ( $modulesByVariant as $variant => $modules ) {
160  $disjunctionConds[] = $db->makeList(
161  [ 'md_skin' => $variant, 'md_module' => $modules ],
163  );
164  }
165 
166  $depsBlobByEntity = [];
167 
168  if ( $disjunctionConds ) {
169  $res = $db->select(
170  'module_deps',
171  [ 'md_module', 'md_skin', 'md_deps' ],
172  $db->makeList( $disjunctionConds, $db::LIST_OR ),
173  __METHOD__
174  );
175 
176  foreach ( $res as $row ) {
177  $entity = "{$row->md_module}|{$row->md_skin}";
178  $depsBlobByEntity[$entity] = $row->md_deps;
179  }
180  }
181 
182  return $depsBlobByEntity;
183  }
184 
188  private function getReplicaDb() {
189  return $this->lb
190  ->getConnectionRef( DB_REPLICA, [], false, ( $this->lb )::CONN_TRX_AUTOCOMMIT );
191  }
192 
196  private function getMasterDb() {
197  return $this->lb
198  ->getConnectionRef( DB_MASTER, [], false, ( $this->lb )::CONN_TRX_AUTOCOMMIT );
199  }
200 
205  private function getEntityNameComponents( $entity ) {
206  $parts = explode( '|', $entity, 2 );
207  if ( count( $parts ) !== 2 ) {
208  throw new InvalidArgumentException( "Invalid module entity '$entity'" );
209  }
210 
211  return $parts;
212  }
213 }
Wikimedia\DependencyStore\SqlModuleDependencyStore\renew
renew( $type, $entities, $ttl)
Set the expiry for the currently tracked dependencies for an entity or set of entities.
Definition: SqlModuleDependencyStore.php:142
Wikimedia\DependencyStore\SqlModuleDependencyStore\getReplicaDb
getReplicaDb()
Definition: SqlModuleDependencyStore.php:188
Wikimedia\DependencyStore\DependencyStoreException
Definition: DependencyStoreException.php:10
Wikimedia\DependencyStore
Definition: DependencyStore.php:21
Wikimedia\DependencyStore\SqlModuleDependencyStore\getMasterDb
getMasterDb()
Definition: SqlModuleDependencyStore.php:196
Wikimedia\DependencyStore\SqlModuleDependencyStore\getEntityNameComponents
getEntityNameComponents( $entity)
Definition: SqlModuleDependencyStore.php:205
Wikimedia\DependencyStore\SqlModuleDependencyStore\__construct
__construct(ILoadBalancer $lb)
Definition: SqlModuleDependencyStore.php:44
$res
$res
Definition: testCompression.php:57
Wikimedia\Rdbms\DBError
Database error base class.
Definition: DBError.php:30
LIST_AND
const LIST_AND
Definition: Defines.php:48
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
Wikimedia\DependencyStore\SqlModuleDependencyStore\storeMulti
storeMulti( $type, array $dataByEntity, $ttl)
Set the currently tracked dependencies for a set of entities.
Definition: SqlModuleDependencyStore.php:71
$dbr
$dbr
Definition: testCompression.php:54
LIST_OR
const LIST_OR
Definition: Defines.php:51
$blob
$blob
Definition: testCompression.php:70
$modules
$modules
Definition: HTMLFormElement.php:13
Wikimedia\DependencyStore\SqlModuleDependencyStore\fetchDependencyBlobs
fetchDependencyBlobs(array $entities, IDatabase $db)
Definition: SqlModuleDependencyStore.php:151
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
DB_MASTER
const DB_MASTER
Definition: defines.php:26
Wikimedia\DependencyStore\SqlModuleDependencyStore\retrieveMulti
retrieveMulti( $type, array $entities)
Get the currently tracked dependencies for a set of entities.
Definition: SqlModuleDependencyStore.php:48
Wikimedia\DependencyStore\SqlModuleDependencyStore
Class for tracking per-entity dependency path lists in the module_deps table.
Definition: SqlModuleDependencyStore.php:37
Wikimedia\DependencyStore\DependencyStore\newEntityDependencies
newEntityDependencies(array $paths=[], $asOf=null)
Definition: DependencyStore.php:39
Wikimedia\Rdbms\DBConnRef
Helper class used for automatically marking an IDatabase connection as reusable (once it no longer ma...
Definition: DBConnRef.php:29
Wikimedia\DependencyStore\SqlModuleDependencyStore\$lb
ILoadBalancer $lb
Definition: SqlModuleDependencyStore.php:39
Wikimedia\Rdbms\IDatabase\select
select( $table, $vars, $conds='', $fname=__METHOD__, $options=[], $join_conds=[])
Execute a SELECT query constructed using the various parameters provided.
Wikimedia\DependencyStore\DependencyStore
Class for tracking per-entity dependency path lists that are expensive to mass compute.
Definition: DependencyStore.php:28
Wikimedia\Rdbms\IDatabase\makeList
makeList(array $a, $mode=self::LIST_COMMA)
Makes an encoded list of strings from an array.
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
$type
$type
Definition: testCompression.php:52