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  // Avoid opening a primary DB connection when it's not needed.
73  // ResourceLoader::saveModuleDependenciesInternal calls this method unconditionally
74  // with empty values most of the time.
75  if ( !$dataByEntity ) {
76  return;
77  }
78  try {
79  $dbw = $this->getPrimaryDB();
80 
81  $depsBlobByEntity = $this->fetchDependencyBlobs( array_keys( $dataByEntity ), $dbw );
82 
83  $rows = [];
84  foreach ( $dataByEntity as $entity => $data ) {
85  list( $module, $variant ) = $this->getEntityNameComponents( $entity );
86  if ( !is_array( $data[self::KEY_PATHS] ) ) {
87  throw new InvalidArgumentException( "Invalid entry for '$entity'" );
88  }
89 
90  // Normalize the list by removing duplicates and sortings
91  $paths = array_values( array_unique( $data[self::KEY_PATHS] ) );
92  sort( $paths, SORT_STRING );
93  $blob = json_encode( $paths, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
94 
95  $existingBlob = $depsBlobByEntity[$entity] ?? null;
96  if ( $blob !== $existingBlob ) {
97  $rows[] = [
98  'md_module' => $module,
99  'md_skin' => $variant,
100  'md_deps' => $blob
101  ];
102  }
103  }
104 
105  // @TODO: use a single query with VALUES()/aliases support in DB wrapper
106  // See https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html
107  foreach ( $rows as $row ) {
108  $dbw->upsert(
109  'module_deps',
110  $row,
111  [ [ 'md_module', 'md_skin' ] ],
112  [
113  'md_deps' => $row['md_deps'],
114  ],
115  __METHOD__
116  );
117  }
118  } catch ( DBError $e ) {
119  throw new DependencyStoreException( $e->getMessage() );
120  }
121  }
122 
123  public function remove( $type, $entities ) {
124  // Avoid opening a primary DB connection when it's not needed.
125  // ResourceLoader::saveModuleDependenciesInternal calls this method unconditionally
126  // with empty values most of the time.
127  if ( !$entities ) {
128  return;
129  }
130  try {
131  $dbw = $this->getPrimaryDB();
132 
133  $disjunctionConds = [];
134  foreach ( (array)$entities as $entity ) {
135  list( $module, $variant ) = $this->getEntityNameComponents( $entity );
136  $disjunctionConds[] = $dbw->makeList(
137  [ 'md_skin' => $variant, 'md_module' => $module ],
139  );
140  }
141 
142  if ( $disjunctionConds ) {
143  $dbw->delete(
144  'module_deps',
145  $dbw->makeList( $disjunctionConds, $dbw::LIST_OR ),
146  __METHOD__
147  );
148  }
149  } catch ( DBError $e ) {
150  throw new DependencyStoreException( $e->getMessage() );
151  }
152  }
153 
154  public function renew( $type, $entities, $ttl ) {
155  // no-op
156  }
157 
163  private function fetchDependencyBlobs( array $entities, IDatabase $db ) {
164  $modulesByVariant = [];
165  foreach ( $entities as $entity ) {
166  list( $module, $variant ) = $this->getEntityNameComponents( $entity );
167  $modulesByVariant[$variant][] = $module;
168  }
169 
170  $disjunctionConds = [];
171  foreach ( $modulesByVariant as $variant => $modules ) {
172  $disjunctionConds[] = $db->makeList(
173  [ 'md_skin' => $variant, 'md_module' => $modules ],
175  );
176  }
177 
178  $depsBlobByEntity = [];
179 
180  if ( $disjunctionConds ) {
181  $res = $db->select(
182  'module_deps',
183  [ 'md_module', 'md_skin', 'md_deps' ],
184  $db->makeList( $disjunctionConds, $db::LIST_OR ),
185  __METHOD__
186  );
187 
188  foreach ( $res as $row ) {
189  $entity = "{$row->md_module}|{$row->md_skin}";
190  $depsBlobByEntity[$entity] = $row->md_deps;
191  }
192  }
193 
194  return $depsBlobByEntity;
195  }
196 
200  private function getReplicaDb() {
201  return $this->lb
202  ->getConnectionRef( DB_REPLICA, [], false, ( $this->lb )::CONN_TRX_AUTOCOMMIT );
203  }
204 
208  private function getPrimaryDb() {
209  return $this->lb
210  ->getConnectionRef( DB_PRIMARY, [], false, ( $this->lb )::CONN_TRX_AUTOCOMMIT );
211  }
212 
217  private function getEntityNameComponents( $entity ) {
218  $parts = explode( '|', $entity, 2 );
219  if ( count( $parts ) !== 2 ) {
220  throw new InvalidArgumentException( "Invalid module entity '$entity'" );
221  }
222 
223  return $parts;
224  }
225 }
LIST_OR
const LIST_OR
Definition: Defines.php:46
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:154
Wikimedia\DependencyStore\SqlModuleDependencyStore\getReplicaDb
getReplicaDb()
Definition: SqlModuleDependencyStore.php:200
Wikimedia\DependencyStore\DependencyStoreException
Definition: DependencyStoreException.php:11
Wikimedia\DependencyStore
Definition: DependencyStore.php:21
Wikimedia\DependencyStore\SqlModuleDependencyStore\getEntityNameComponents
getEntityNameComponents( $entity)
Definition: SqlModuleDependencyStore.php:217
LIST_AND
const LIST_AND
Definition: Defines.php:43
Wikimedia\DependencyStore\SqlModuleDependencyStore\__construct
__construct(ILoadBalancer $lb)
Definition: SqlModuleDependencyStore.php:44
$res
$res
Definition: testCompression.php:57
Wikimedia\Rdbms\DBError
Database error base class @newable.
Definition: DBError.php:32
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
Wikimedia\DependencyStore\SqlModuleDependencyStore\getPrimaryDb
getPrimaryDb()
Definition: SqlModuleDependencyStore.php:208
$blob
$blob
Definition: testCompression.php:70
$modules
$modules
Definition: HTMLFormElement.php:15
Wikimedia\DependencyStore\SqlModuleDependencyStore\fetchDependencyBlobs
fetchDependencyBlobs(array $entities, IDatabase $db)
Definition: SqlModuleDependencyStore.php:163
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
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
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
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