mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
added caching layer
This commit is contained in:
@@ -39,28 +39,104 @@ class XList extends Action
|
||||
// just for getting group based presence list based on permissions
|
||||
public function action(UtopiaResponse $response, Database $dbForProject, Authorization $authorization, Document $user): void
|
||||
{
|
||||
$requestingUserId = $user->getId();
|
||||
$hostname = $dbForProject->getAdapter()->getHostname();
|
||||
$usersCacheKey = \sprintf(
|
||||
'%s-cache-%s:%s:%s:collection:presence',
|
||||
$dbForProject->getCacheName(),
|
||||
$hostname ?? '',
|
||||
$dbForProject->getNamespace(),
|
||||
$dbForProject->getTenant()
|
||||
);
|
||||
$presenceLogsCacheKey = \sprintf(
|
||||
'%s-cache-%s:%s:%s:collection:presenceLogs:user:%s',
|
||||
$dbForProject->getCacheName(),
|
||||
$hostname ?? '',
|
||||
$dbForProject->getNamespace(),
|
||||
$dbForProject->getTenant(),
|
||||
$requestingUserId
|
||||
);
|
||||
$ttl = 60;
|
||||
try {
|
||||
$presenceLogs = [];
|
||||
$totalStart = microtime(true);
|
||||
$userStart = microtime(true);
|
||||
|
||||
$users = $authorization->skip(fn () => $dbForProject->find('presence', [
|
||||
Query::limit(10000),
|
||||
]));
|
||||
foreach ($users as $user) {
|
||||
$presenceCacheStart = microtime(true);
|
||||
$cachedPresenceLogs = $dbForProject->getCache()->load($presenceLogsCacheKey, $ttl);
|
||||
if($cachedPresenceLogs !== null && $cachedPresenceLogs !== false && \is_array($cachedPresenceLogs)){
|
||||
$presenceLogs = array_map(fn($doc) => new Document($doc), $cachedPresenceLogs);
|
||||
$presenceLogEnd = microtime(true) - $presenceCacheStart;
|
||||
$totalEnd = microtime(true) - $totalStart;
|
||||
Console::info(sprintf(
|
||||
"Cache | Requesting User [%s] | [Total][time] %.2f ms | [Users][time] %.2f ms | [PresenceCache][time] %.2f ms\n",
|
||||
$requestingUserId,
|
||||
$totalEnd * 1000,
|
||||
0,
|
||||
$presenceLogEnd * 1000
|
||||
));
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'presences' => $presenceLogs,
|
||||
'total' => \count($presenceLogs),
|
||||
]), UtopiaResponse::MODEL_PRESENCE_LIST);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$cachedUsers = $dbForProject->getCache()->load($usersCacheKey, $ttl);
|
||||
if($cachedUsers !== null && $cachedUsers !== false && \is_array($cachedUsers)){
|
||||
$users = array_map(fn($doc) => new Document($doc), $cachedUsers);
|
||||
}
|
||||
else{
|
||||
$users = $authorization->skip(fn () => $dbForProject->find('presence', [
|
||||
Query::limit(10000),
|
||||
]));
|
||||
|
||||
// saving to cache
|
||||
$documentsArray = \array_map(function ($doc) {
|
||||
return $doc->getArrayCopy();
|
||||
}, $users);
|
||||
$dbForProject->getCache()->save($usersCacheKey, $documentsArray);
|
||||
}
|
||||
|
||||
$userEnd = microtime(true) - $userStart;
|
||||
$presenceLogStart = microtime(true);
|
||||
foreach ($users as $presenceUser) {
|
||||
// $presenceCacheKey = \sprintf(
|
||||
// '%s-cache-%s:%s:%s:collection:presenceLogs:user:%s',
|
||||
// $dbForProject->getCacheName(),
|
||||
// $hostname ?? '',
|
||||
// $dbForProject->getNamespace(),
|
||||
// $dbForProject->getTenant(),
|
||||
// $presenceUser['userId'] ?? ''
|
||||
// );
|
||||
|
||||
$presenceLog = $dbForProject->findOne('presenceLogs', [
|
||||
Query::equal('userId', [$user['userId']]),
|
||||
Query::orderDesc('$updatedAt'),
|
||||
// Tie-breaker: `$updatedAt` has only millisecond precision.
|
||||
Query::orderDesc('$id'),
|
||||
Query::limit(1),
|
||||
]);
|
||||
Query::equal('userId', [$presenceUser['userId']]),
|
||||
Query::orderDesc('$updatedAt'),
|
||||
// Tie-breaker: `$updatedAt` has only millisecond precision.
|
||||
Query::orderDesc('$id'),
|
||||
Query::limit(1),
|
||||
]);
|
||||
|
||||
$presenceLogs[] = $presenceLog;
|
||||
}
|
||||
$dbForProject->getCache()->save($presenceLogsCacheKey, $presenceLogs);
|
||||
$presenceLogEnd = microtime(true) - $presenceLogStart;
|
||||
$totalEnd = microtime(true) - $totalStart;
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order column '{$e->getAttribute()}' had a null value. Cursor pagination requires all rows order column values are non-null.");
|
||||
} catch (QueryException) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID);
|
||||
}
|
||||
|
||||
Console::info(sprintf(
|
||||
"Requesting User [%s] | [Total][time] %.2f ms | [Users][time] %.2f ms | [PresenceLogs][time] %.2f ms\n",
|
||||
$requestingUserId,
|
||||
$totalEnd * 1000,
|
||||
$userEnd * 1000,
|
||||
$presenceLogEnd * 1000
|
||||
));
|
||||
$response->dynamic(new Document([
|
||||
'presences' => $presenceLogs,
|
||||
'total' => \count($presenceLogs),
|
||||
|
||||
@@ -763,16 +763,19 @@ abstract class PresenceBase extends Scope
|
||||
|
||||
// Pick 2 viewers from each group: first two indices within group.
|
||||
$viewerIndices = [];
|
||||
for ($g = 0; $g < $numGroups; $g++) {
|
||||
for ($g = 0; $g < $numGroups && \count($viewerIndices) < $viewerCount; $g++) {
|
||||
$base = $g * $groupSize;
|
||||
|
||||
$viewerIndices[] = $base;
|
||||
if ($viewerCount >= 10) {
|
||||
// keep exactly 10; should trigger only if group math changes
|
||||
if (\count($viewerIndices) >= $viewerCount) {
|
||||
break;
|
||||
}
|
||||
$viewerIndices[] = $base + 1;
|
||||
|
||||
// Add the next index within the group if it exists.
|
||||
if ($base + 1 < $totalUsers) {
|
||||
$viewerIndices[] = $base + 1;
|
||||
}
|
||||
}
|
||||
$viewerIndices = \array_slice($viewerIndices, 0, $viewerCount);
|
||||
|
||||
$statusSampleOwnerOffsets = [0, 50, 99];
|
||||
|
||||
@@ -828,11 +831,12 @@ abstract class PresenceBase extends Scope
|
||||
// Allow realtime write to settle.
|
||||
\usleep(100000);
|
||||
|
||||
for ($occurrence = 0; $occurrence < $occurrences; $occurrence++) {
|
||||
foreach ($viewerIndices as $viewerSampleIndex) {
|
||||
$viewer = $users[$viewerSampleIndex];
|
||||
$group = (int) \floor($viewerSampleIndex / $groupSize);
|
||||
// Fetch listPresence `occurrences` times per viewer, in-order.
|
||||
foreach ($viewerIndices as $viewerSampleIndex) {
|
||||
$viewer = $users[$viewerSampleIndex];
|
||||
$group = (int) \floor($viewerSampleIndex / $groupSize);
|
||||
|
||||
for ($occurrence = 0; $occurrence < $occurrences; $occurrence++) {
|
||||
$benchmark = $this->fetchPresenceListAs($viewer);
|
||||
$this->assertEquals(200, $benchmark['response']['headers']['status-code']);
|
||||
$this->assertGreaterThan(0.0, $benchmark['elapsedMs']);
|
||||
@@ -861,10 +865,11 @@ abstract class PresenceBase extends Scope
|
||||
}
|
||||
|
||||
Console::info(\sprintf(
|
||||
'[Presence Benchmark F] cycle=%d occurrence=%d listCallsThisOcc=%d',
|
||||
'[Presence Benchmark F] cycle=%d viewerIndex=%d viewerGroup=%d occurrences=%d',
|
||||
$cycle,
|
||||
$occurrence + 1,
|
||||
$viewerCount
|
||||
$viewerSampleIndex,
|
||||
$group,
|
||||
$occurrences
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user