Skip to content
This repository was archived by the owner on Mar 20, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/Model/Project.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ protected function getMetadata(): ?array {
* @return bool
*/
public function exists(): bool {
return !empty( $this->getDomain() );
return !empty( $this->getDomain() )
&& $this->repository->checkReplication($this->getBasicInfo()['dbName']);
}

/**
Expand Down Expand Up @@ -148,9 +149,9 @@ public function getUrl( bool $withTrailingSlash = true ): string {
/**
* @param Page|string $page Full page title including namespace, or a Page object.
* @param bool $useUnnormalizedPageTitle Use the unnormalized page title to avoid
* an API call. This should be used only if you fetched the page title via other
* means (SQL query), and is not from user input alone. Only applicable if $page
* is a Page object.
* an API call. This should be used only if you fetched the page title via other
* means (SQL query), and is not from user input alone. Only applicable if $page
* is a Page object.
* @return string
*/
public function getUrlForPage( Page|string $page, bool $useUnnormalizedPageTitle = false ): string {
Expand Down Expand Up @@ -356,5 +357,5 @@ public function userHasOptedIn( User $user ): bool {
*/
public function getTableName( string $tableName, ?string $tableExtension = null ): string {
return $this->getRepository()->getTableName( $this->getDatabaseName(), $tableName, $tableExtension );
}
}
}
30 changes: 30 additions & 0 deletions src/Repository/GlobalContribsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public function globalEditCounts( User $user ): ?array {

// Pre-populate all projects' metadata, to prevent each project call from fetching it.
$this->caProject->getRepository()->getAll();

$this->checkReplicationAllProjects();

// Compile the output.
$out = [];
Expand Down Expand Up @@ -110,6 +112,34 @@ protected function globalEditCountsFromCentralAuth( User $user ): ?array {
// Cache and return.
return $this->setCache( $cacheKey, $out );
}

/**
* Get, slice by slice, the list of projects that are actually replicated.
* Takes about 0.5s per slice.
* @return bool[] Keyed by database name, all values are true.
*/
public function checkReplicationAllProjects(): array {
$cacheKey = $this->getCacheKey( "global_replication_check" );
if ( $this->cache->hasItem( $cacheKey ) ) {
return $this->cache->getItem( $cacheKey )->get();
}
$result = [];
$exists = true;
$i = 0;
$sql = "SELECT DISTINCT table_schema
FROM information_schema.tables";
while ( $exists ) {
$i += 1;
try {
$queryResult = $this->executeProjectsQuery( "s$i", $sql )->fetchFirstColumn();
$result = array_merge( $result, $queryResult );
} catch ( \Throwable ) {
$exists = false;
}
}
return $this->setCache( $cacheKey, $result, 'PT1H' );
}


/**
* Loop through the given dbNames and create Project objects for each.
Expand Down
42 changes: 41 additions & 1 deletion src/Repository/ProjectRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,47 @@ public function getOne( string $project ): ?array {
// Cache for one hour and return.
return $this->setCache( $cacheKey, $basicInfo, 'PT1H' );
}


/**
* Is this project actually replicated? Sometimes projets aren't,
* despite being listed in meta_p.wiki. See T322466.
* @param string $project Database name, without _p.
* @return bool
*/
public function checkReplication( string $project ): bool {
if ( '' == $project ) {
// This means we failed to getBasicInfo. Let's try and AGF.
// Plus, keeps tests from breaking down.
return true;
}
$cacheKey = $this->getCacheKey( $project, "replication_check" );
if ( $this->cache->hasItem( $cacheKey )) {
return $this->cache->getItem( $cacheKey )->get();
}
// GlobalContribs preloads replication checks for *all* projects
$allProjectsCacheKey = $this->getCacheKey( '', "global_replication_check" );
if ( $this->cache->hasItem( $allProjectsCacheKey )) {
$globalReplicationChecks = $this->cache->getItem( $allProjectsCacheKey )->get();
return array_key_exists( $project, $globalReplicationChecks );
}
$dbList = $this->getDbList();
if ( !array_key_exists( $project, $dbList )) {
$result = false;
} else {
$dbSlice = $dbList[$project];
$sql = "SELECT 1
FROM information_schema.tables
WHERE table_schema = :project
LIMIT 1";
$queryResult = $this->executeProjectsQuery( $dbSlice, $sql, [
'project' => $project . "_p",
] )->fetchAssociative();
$result = ( 1 == count( $queryResult ));
}
// Cache for 1h and return
return $this->setCache( $cacheKey, $result, 'PT1H' ); // feels long to me, but as long as getOne
}

/**
* Get metadata about a project, including the 'dbName', 'url' and 'lang'
*
Expand Down
2 changes: 2 additions & 0 deletions tests/Model/GlobalContribsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public function testGlobalEdits(): void {
'dbName' => 'wiki1',
'url' => 'https://wiki1.example.org',
] );
$wiki1Repo->method('checkReplication')
->willReturn(true);
$wiki1 = new Project( 'wiki1' );
$wiki1->setRepository( $wiki1Repo );

Expand Down
2 changes: 2 additions & 0 deletions tests/TestAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public function getProjectRepo(): MockObject {
'dbName' => 'test_wiki',
'lang' => 'en',
] );
$repo->method('checkReplication')
->willReturn(true);
return $repo;
}

Expand Down
Loading