aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/DB/Connection.php
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@winzerhof-wurst.at>2024-02-02 16:53:59 +0100
committerChristoph Wurst <christoph@winzerhof-wurst.at>2024-02-19 10:13:24 +0100
commit1f46e4b854cde5170c996a8f168f34054e39754a (patch)
tree49426d9251692f58ff6bbedfcf0dbe4e322744e4 /lib/private/DB/Connection.php
parent50fffa8d95aa2b1adc61f69cc76408300438213d (diff)
downloadnextcloud-server-1f46e4b854cde5170c996a8f168f34054e39754a.tar.gz
nextcloud-server-1f46e4b854cde5170c996a8f168f34054e39754a.zip
fix(db): Let dirty writes cool off
We can assume that after a few seconds a read will be clean again. This is helpful for false warnings in long running processes. Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'lib/private/DB/Connection.php')
-rw-r--r--lib/private/DB/Connection.php23
1 files changed, 20 insertions, 3 deletions
diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php
index ed322bc90f0..994c89ad6fc 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -55,7 +55,9 @@ use OCP\Diagnostics\IEventLogger;
use OCP\IRequestId;
use OCP\PreConditionNotMetException;
use OCP\Profiler\IProfiler;
+use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;
+use function in_array;
class Connection extends PrimaryReadReplicaConnection {
/** @var string */
@@ -67,6 +69,8 @@ class Connection extends PrimaryReadReplicaConnection {
/** @var SystemConfig */
private $systemConfig;
+ private ClockInterface $clock;
+
private LoggerInterface $logger;
protected $lockedTable = null;
@@ -83,6 +87,7 @@ class Connection extends PrimaryReadReplicaConnection {
protected ?float $transactionActiveSince = null;
+ /** @var array<string, int> */
protected $tableDirtyWrites = [];
/**
@@ -110,6 +115,7 @@ class Connection extends PrimaryReadReplicaConnection {
$this->tablePrefix = $params['tablePrefix'];
$this->systemConfig = \OC::$server->getSystemConfig();
+ $this->clock = \OCP\Server::get(ClockInterface::class);
$this->logger = \OC::$server->get(LoggerInterface::class);
/** @var \OCP\Profiler\IProfiler */
@@ -265,10 +271,19 @@ class Connection extends PrimaryReadReplicaConnection {
*/
public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile $qcp = null): Result {
$tables = $this->getQueriedTables($sql);
+ $now = $this->clock->now()->getTimestamp();
+ $dirtyTableWrites = [];
+ foreach ($tables as $table) {
+ $lastAccess = $this->tableDirtyWrites[$table] ?? 0;
+ // Only very recent writes are considered dirty
+ if ($lastAccess >= ($now - 3)) {
+ $dirtyTableWrites[] = $table;
+ }
+ }
if ($this->isTransactionActive()) {
// Transacted queries go to the primary. The consistency of the primary guarantees that we can not run
// into a dirty read.
- } elseif (count(array_intersect($this->tableDirtyWrites, $tables)) === 0) {
+ } elseif (count($dirtyTableWrites) === 0) {
// No tables read that could have been written already in the same request and no transaction active
// so we can switch back to the replica for reading as long as no writes happen that switch back to the primary
// We cannot log here as this would log too early in the server boot process
@@ -280,7 +295,7 @@ class Connection extends PrimaryReadReplicaConnection {
(int) ($this->systemConfig->getValue('loglevel_dirty_database_queries', null) ?? 0),
'dirty table reads: ' . $sql,
[
- 'tables' => $this->tableDirtyWrites,
+ 'tables' => array_keys($this->tableDirtyWrites),
'reads' => $tables,
'exception' => new \Exception(),
],
@@ -335,7 +350,9 @@ class Connection extends PrimaryReadReplicaConnection {
*/
public function executeStatement($sql, array $params = [], array $types = []): int {
$tables = $this->getQueriedTables($sql);
- $this->tableDirtyWrites = array_unique(array_merge($this->tableDirtyWrites, $tables));
+ foreach ($tables as $table) {
+ $this->tableDirtyWrites[$table] = $this->clock->now()->getTimestamp();
+ }
$sql = $this->replaceTablePrefix($sql);
$sql = $this->adapter->fixupStatement($sql);
$this->queriesExecuted++;