Skip to content

Commit

Permalink
Merge pull request #36643 from nextcloud/fs-setup-instrimuntion
Browse files Browse the repository at this point in the history
more filesystem setup performance instrumentation
  • Loading branch information
icewind1991 authored Feb 13, 2023
2 parents e0b2c93 + 91d5f6a commit 936f634
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 28 deletions.
38 changes: 25 additions & 13 deletions lib/private/Files/Config/MountProviderCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

use OC\Hooks\Emitter;
use OC\Hooks\EmitterTrait;
use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\IHomeMountProvider;
use OCP\Files\Config\IMountProvider;
use OCP\Files\Config\IMountProviderCollection;
Expand Down Expand Up @@ -65,13 +66,29 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
/** @var callable[] */
private $mountFilters = [];

private IEventLogger $eventLogger;

/**
* @param \OCP\Files\Storage\IStorageFactory $loader
* @param IUserMountCache $mountCache
*/
public function __construct(IStorageFactory $loader, IUserMountCache $mountCache) {
public function __construct(
IStorageFactory $loader,
IUserMountCache $mountCache,
IEventLogger $eventLogger
) {
$this->loader = $loader;
$this->mountCache = $mountCache;
$this->eventLogger = $eventLogger;
}

private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
$class = str_replace('\\', '_', get_class($provider));
$uid = $user->getUID();
$this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
$mounts = $provider->getMountsForUser($user, $loader) ?? [];
$this->eventLogger->end('fs:setup:provider:' . $class);
return $mounts;
}

/**
Expand All @@ -82,11 +99,8 @@ public function __construct(IStorageFactory $loader, IUserMountCache $mountCache
private function getUserMountsForProviders(IUser $user, array $providers): array {
$loader = $this->loader;
$mounts = array_map(function (IMountProvider $provider) use ($user, $loader) {
return $provider->getMountsForUser($user, $loader);
return $this->getMountsFromProvider($provider, $user, $loader);
}, $providers);
$mounts = array_filter($mounts, function ($result) {
return is_array($result);
});
$mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) {
return array_merge($mounts, $providerMounts);
}, []);
Expand Down Expand Up @@ -121,24 +135,22 @@ public function addMountForUser(IUser $user, IMountManager $mountManager, callab
return (get_class($provider) === 'OCA\Files_Sharing\MountProvider');
});
foreach ($firstProviders as $provider) {
$mounts = $provider->getMountsForUser($user, $this->loader);
if (is_array($mounts)) {
$firstMounts = array_merge($firstMounts, $mounts);
}
$mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
$firstMounts = array_merge($firstMounts, $mounts);
}
$firstMounts = $this->filterMounts($user, $firstMounts);
array_walk($firstMounts, [$mountManager, 'addMount']);

$lateMounts = [];
foreach ($lastProviders as $provider) {
$mounts = $provider->getMountsForUser($user, $this->loader);
if (is_array($mounts)) {
$lateMounts = array_merge($lateMounts, $mounts);
}
$mounts = $this->getMountsFromProvider($provider, $user, $this->loader);
$lateMounts = array_merge($lateMounts, $mounts);
}

$lateMounts = $this->filterMounts($user, $lateMounts);
$this->eventLogger->start("fs:setup:add-mounts", "Add mounts to the filesystem");
array_walk($lateMounts, [$mountManager, 'addMount']);
$this->eventLogger->end("fs:setup:add-mounts");

return array_merge($lateMounts, $firstMounts);
}
Expand Down
12 changes: 11 additions & 1 deletion lib/private/Files/Config/UserMountCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use OCP\Cache\CappedMemoryCache;
use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Diagnostics\IEventLogger;
use OCP\Files\Config\ICachedMountFileInfo;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IUserMountCache;
Expand All @@ -56,19 +57,27 @@ class UserMountCache implements IUserMountCache {
private LoggerInterface $logger;
/** @var CappedMemoryCache<array> */
private CappedMemoryCache $cacheInfoCache;
private IEventLogger $eventLogger;

/**
* UserMountCache constructor.
*/
public function __construct(IDBConnection $connection, IUserManager $userManager, LoggerInterface $logger) {
public function __construct(
IDBConnection $connection,
IUserManager $userManager,
LoggerInterface $logger,
IEventLogger $eventLogger
) {
$this->connection = $connection;
$this->userManager = $userManager;
$this->logger = $logger;
$this->eventLogger = $eventLogger;
$this->cacheInfoCache = new CappedMemoryCache();
$this->mountsForUsers = new CappedMemoryCache();
}

public function registerMounts(IUser $user, array $mounts, array $mountProviderClasses = null) {
$this->eventLogger->start('fs:setup:user:register', 'Registering mounts for user');
// filter out non-proper storages coming from unit tests
$mounts = array_filter($mounts, function (IMountPoint $mount) {
return $mount instanceof SharedMount || ($mount->getStorage() && $mount->getStorage()->getCache());
Expand Down Expand Up @@ -134,6 +143,7 @@ public function registerMounts(IUser $user, array $mounts, array $mountProviderC
foreach ($changedMounts as $mount) {
$this->updateCachedMount($mount);
}
$this->eventLogger->end('fs:setup:user:register');
}

/**
Expand Down
39 changes: 33 additions & 6 deletions lib/private/Files/SetupManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ public function setupForUser(IUser $user): void {
}
$this->setupUsersComplete[] = $user->getUID();

$this->eventLogger->start('fs:setup:user:full', 'Setup full filesystem for user');

if (!isset($this->setupUserMountProviders[$user->getUID()])) {
$this->setupUserMountProviders[$user->getUID()] = [];
}
Expand All @@ -226,6 +228,7 @@ public function setupForUser(IUser $user): void {
});
});
$this->afterUserFullySetup($user, $previouslySetupProviders);
$this->eventLogger->end('fs:setup:user:full');
}

/**
Expand All @@ -237,6 +240,8 @@ private function oneTimeUserSetup(IUser $user) {
}
$this->setupUsers[] = $user->getUID();

$this->eventLogger->start('fs:setup:user:onetime', 'Onetime filesystem for user');

$this->setupBuiltinWrappers();

$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
Expand All @@ -250,14 +255,18 @@ private function oneTimeUserSetup(IUser $user) {
Filesystem::initInternal($userDir);

if ($this->lockdownManager->canAccessFilesystem()) {
$this->eventLogger->start('fs:setup:user:home', 'Setup home filesystem for user');
// home mounts are handled separate since we need to ensure this is mounted before we call the other mount providers
$homeMount = $this->mountProviderCollection->getHomeMountForUser($user);
$this->mountManager->addMount($homeMount);

if ($homeMount->getStorageRootId() === -1) {
$this->eventLogger->start('fs:setup:user:home:scan', 'Scan home filesystem for user');
$homeMount->getStorage()->mkdir('');
$homeMount->getStorage()->getScanner()->scan('');
$this->eventLogger->end('fs:setup:user:home:scan');
}
$this->eventLogger->end('fs:setup:user:home');
} else {
$this->mountManager->addMount(new MountPoint(
new NullStorage([]),
Expand All @@ -271,12 +280,15 @@ private function oneTimeUserSetup(IUser $user) {
}

$this->listenForNewMountProviders();

$this->eventLogger->end('fs:setup:user:onetime');
}

/**
* Final housekeeping after a user has been fully setup
*/
private function afterUserFullySetup(IUser $user, array $previouslySetupProviders): void {
$this->eventLogger->start('fs:setup:user:full:post', 'Housekeeping after user is setup');
$userRoot = '/' . $user->getUID() . '/';
$mounts = $this->mountManager->getAll();
$mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) {
Expand All @@ -296,6 +308,7 @@ private function afterUserFullySetup(IUser $user, array $previouslySetupProvider
$this->cache->set($user->getUID(), true, $cacheDuration);
$this->fullSetupRequired[$user->getUID()] = false;
}
$this->eventLogger->end('fs:setup:user:full:post');
}

/**
Expand All @@ -312,17 +325,17 @@ private function setupForUserWith(IUser $user, callable $mountCallback): void {
$this->oneTimeUserSetup($user);
}

$this->eventLogger->start('setup_fs', 'Setup filesystem');

if ($this->lockdownManager->canAccessFilesystem()) {
$mountCallback();
}
$this->eventLogger->start('fs:setup:user:post-init-mountpoint', 'post_initMountPoints legacy hook');
\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', ['user' => $user->getUID()]);
$this->eventLogger->end('fs:setup:user:post-init-mountpoint');

$userDir = '/' . $user->getUID() . '/files';
$this->eventLogger->start('fs:setup:user:setup-hook', 'setup legacy hook');
OC_Hook::emit('OC_Filesystem', 'setup', ['user' => $user->getUID(), 'user_dir' => $userDir]);

$this->eventLogger->end('setup_fs');
$this->eventLogger->end('fs:setup:user:setup-hook');
}

/**
Expand All @@ -335,7 +348,7 @@ public function setupRoot(): void {
}
$this->rootSetup = true;

$this->eventLogger->start('setup_root_fs', 'Setup root filesystem');
$this->eventLogger->start('fs:setup:root', 'Setup root filesystem');

$this->setupBuiltinWrappers();

Expand All @@ -344,7 +357,7 @@ public function setupRoot(): void {
$this->mountManager->addMount($rootMountProvider);
}

$this->eventLogger->end('setup_root_fs');
$this->eventLogger->end('fs:setup:root');
}

/**
Expand Down Expand Up @@ -413,6 +426,9 @@ public function setupForPath(string $path, bool $includeChildren = false): void
$this->oneTimeUserSetup($user);
}

$this->eventLogger->start('fs:setup:user:path', "Setup $path filesystem for user");
$this->eventLogger->start('fs:setup:user:path:find', "Find mountpoint for $path");

$mounts = [];
if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
$currentProviders[] = $cachedMount->getMountProvider();
Expand All @@ -421,13 +437,16 @@ public function setupForPath(string $path, bool $includeChildren = false): void
$mounts = $this->mountProviderCollection->getUserMountsForProviderClasses($user, [$cachedMount->getMountProvider()]);
} else {
$this->logger->debug("mount at " . $cachedMount->getMountPoint() . " has no provider set, performing full setup");
$this->eventLogger->end('fs:setup:user:path:find');
$this->setupForUser($user);
$this->eventLogger->end('fs:setup:user:path');
return;
}
}

if ($includeChildren) {
$subCachedMounts = $this->userMountCache->getMountsInPath($user, $path);
$this->eventLogger->end('fs:setup:user:path:find');

$needsFullSetup = array_reduce($subCachedMounts, function (bool $needsFullSetup, ICachedMountInfo $cachedMountInfo) {
return $needsFullSetup || $cachedMountInfo->getMountProvider() === '';
Expand All @@ -436,6 +455,7 @@ public function setupForPath(string $path, bool $includeChildren = false): void
if ($needsFullSetup) {
$this->logger->debug("mount has no provider set, performing full setup");
$this->setupForUser($user);
$this->eventLogger->end('fs:setup:user:path');
return;
} else {
foreach ($subCachedMounts as $cachedMount) {
Expand All @@ -446,6 +466,8 @@ public function setupForPath(string $path, bool $includeChildren = false): void
}
}
}
} else {
$this->eventLogger->end('fs:setup:user:path:find');
}

if (count($mounts)) {
Expand All @@ -456,6 +478,7 @@ public function setupForPath(string $path, bool $includeChildren = false): void
} elseif (!$this->isSetupStarted($user)) {
$this->oneTimeUserSetup($user);
}
$this->eventLogger->end('fs:setup:user:path');
}

private function fullSetupRequired(IUser $user): bool {
Expand Down Expand Up @@ -488,6 +511,8 @@ public function setupForProvider(string $path, array $providers): void {
return;
}

$this->eventLogger->start('fs:setup:user:providers', "Setup filesystem for " . implode(', ', $providers));

// home providers are always used
$providers = array_filter($providers, function (string $provider) {
return !is_subclass_of($provider, IHomeMountProvider::class);
Expand All @@ -504,6 +529,7 @@ public function setupForProvider(string $path, array $providers): void {
if (!$this->isSetupStarted($user)) {
$this->oneTimeUserSetup($user);
}
$this->eventLogger->end('fs:setup:user:providers');
return;
} else {
$this->setupUserMountProviders[$user->getUID()] = array_merge($setupProviders, $providers);
Expand All @@ -514,6 +540,7 @@ public function setupForProvider(string $path, array $providers): void {
$this->setupForUserWith($user, function () use ($mounts) {
array_walk($mounts, [$this->mountManager, 'addMount']);
});
$this->eventLogger->end('fs:setup:user:providers');
}

public function tearDown() {
Expand Down
12 changes: 5 additions & 7 deletions lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
use OC\Federation\CloudFederationFactory;
use OC\Federation\CloudFederationProviderManager;
use OC\Federation\CloudIdManager;
use OC\Files\Config\MountProviderCollection;
use OC\Files\Config\UserMountCache;
use OC\Files\Config\UserMountCacheListener;
use OC\Files\Lock\LockManager;
Expand Down Expand Up @@ -946,11 +947,7 @@ public function __construct($webRoot, \OC\Config $config) {
$this->registerDeprecatedAlias('DateTimeFormatter', IDateTimeFormatter::class);

$this->registerService(IUserMountCache::class, function (ContainerInterface $c) {
$mountCache = new UserMountCache(
$c->get(IDBConnection::class),
$c->get(IUserManager::class),
$c->get(LoggerInterface::class)
);
$mountCache = $c->get(UserMountCache::class);
$listener = new UserMountCacheListener($mountCache);
$listener->listen($c->get(IUserManager::class));
return $mountCache;
Expand All @@ -959,9 +956,10 @@ public function __construct($webRoot, \OC\Config $config) {
$this->registerDeprecatedAlias('UserMountCache', IUserMountCache::class);

$this->registerService(IMountProviderCollection::class, function (ContainerInterface $c) {
$loader = \OC\Files\Filesystem::getLoader();
$loader = $c->get(IStorageFactory::class);
$mountCache = $c->get(IUserMountCache::class);
$manager = new \OC\Files\Config\MountProviderCollection($loader, $mountCache);
$eventLogger = $c->get(IEventLogger::class);
$manager = new MountProviderCollection($loader, $mountCache, $eventLogger);

// builtin providers

Expand Down
3 changes: 2 additions & 1 deletion tests/lib/Files/Config/UserMountCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use OC\Files\Storage\Storage;
use OCP\Cache\CappedMemoryCache;
use OC\User\Manager;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\ICachedMountInfo;
use OCP\ICacheFactory;
Expand Down Expand Up @@ -67,7 +68,7 @@ protected function setUp(): void {
$userBackend->createUser('u2', '');
$userBackend->createUser('u3', '');
$this->userManager->registerBackend($userBackend);
$this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class));
$this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class), $this->createMock(IEventLogger::class));
}

protected function tearDown(): void {
Expand Down

0 comments on commit 936f634

Please sign in to comment.