Skip to content

Commit

Permalink
Listen to cache event for managing metadata
Browse files Browse the repository at this point in the history
Signed-off-by: Louis Chemineau <[email protected]>
  • Loading branch information
artonge committed Oct 18, 2022
1 parent 94ded14 commit c3fd7c2
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 68 deletions.
12 changes: 6 additions & 6 deletions core/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@
use OC\TagManager;
use OCP\AppFramework\App;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\Events\NodeRemovedFromCache;
use OCP\Files\Cache\CacheEntryInsertedEvent;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Cache\CacheEntryUpdatedEvent;
use OCP\IDBConnection;
use OCP\User\Events\BeforeUserDeletedEvent;
use OCP\User\Events\UserDeletedEvent;
Expand Down Expand Up @@ -327,11 +327,11 @@ function (GenericEvent $event) use ($container) {
$config = $container->get(IConfig::class);
if ($config->getSystemValueBool('enable_file_metadata', true)) {
/** @psalm-suppress InvalidArgument */
$eventDispatcher->addServiceListener(NodeDeletedEvent::class, FileEventListener::class);
$eventDispatcher->addServiceListener(CacheEntryRemovedEvent::class, FileEventListener::class);
/** @psalm-suppress InvalidArgument */
$eventDispatcher->addServiceListener(NodeRemovedFromCache::class, FileEventListener::class);
$eventDispatcher->addServiceListener(CacheEntryInsertedEvent::class, FileEventListener::class);
/** @psalm-suppress InvalidArgument */
$eventDispatcher->addServiceListener(NodeWrittenEvent::class, FileEventListener::class);
$eventDispatcher->addServiceListener(CacheEntryUpdatedEvent::class, FileEventListener::class);
}

// Tags
Expand Down
96 changes: 34 additions & 62 deletions lib/private/Metadata/FileEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,89 +21,61 @@

namespace OC\Metadata;

use OC\Files\Filesystem;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\Events\NodeRemovedFromCache;
use OCP\Files\Cache\CacheEntryInsertedEvent;
use OCP\Files\Cache\CacheEntryRemovedEvent;
use OCP\Files\Cache\CacheEntryUpdatedEvent;
use OCP\Files\File;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\FileInfo;
use Psr\Log\LoggerInterface;
use OCP\Files\IRootFolder;

/**
* @template-implements IEventListener<NodeRemovedFromCache>
* @template-implements IEventListener<NodeDeletedEvent>
* @template-implements IEventListener<NodeWrittenEvent>
* @template-implements IEventListener<CacheEntryRemovedEvent>
* @template-implements IEventListener<CacheEntryInsertedEvent>
*/
class FileEventListener implements IEventListener {
private IRootFolder $rootFolder;
private IMetadataManager $manager;
private LoggerInterface $logger;

public function __construct(IMetadataManager $manager, LoggerInterface $logger) {
public function __construct(
IRootFolder $rootFolder,
IMetadataManager $manager
) {
$this->rootFolder = $rootFolder;
$this->manager = $manager;
$this->logger = $logger;
}

private function shouldExtractMetadata(Node $node): bool {
try {
if ($node->getMimetype() === 'httpd/unix-directory') {
return false;
}
} catch (NotFoundException $e) {
return false;
}
if ($node->getSize(false) <= 0) {
return false;
}

$path = $node->getPath();
return $this->isCorrectPath($path);
}

private function isCorrectPath(string $path): bool {
// TODO make this more dynamic, we have the same issue in other places
return !str_starts_with($path, 'appdata_') && !str_starts_with($path, 'files_versions/') && !str_starts_with($path, 'files_trashbin/');
return !str_starts_with($path, 'appdata_') && !str_starts_with($path, 'files_versions/');
}

/**
* @param CacheEntryRemovedEvent|CacheEntryInsertedEvent $event
*/
public function handle(Event $event): void {
if ($event instanceof NodeRemovedFromCache) {
if (!$this->isCorrectPath($event->getPath())) {
// Don't listen to paths for which we don't extract metadata
return;
}
$view = Filesystem::getView();
if (!$view) {
// Should not happen since a scan in the user folder should setup
// the file system.
$e = new \Exception(); // don't trigger, just get backtrace
$this->logger->error('Detecting deletion of a file with possible metadata but file system setup is not setup', [
'exception' => $e,
'app' => 'metadata'
]);
return;
}
$info = $view->getFileInfo($event->getPath());
if ($info && $info->getType() === FileInfo::TYPE_FILE) {
$this->manager->clearMetadata($info->getId());
}
if ($event->getStorage()->is_dir($event->getPath())) {
return;
}

if ($event instanceof NodeDeletedEvent) {
$node = $event->getNode();
if ($this->shouldExtractMetadata($node)) {
/** @var File $node */
$this->manager->clearMetadata($event->getNode()->getId());
}
if (!$this->isCorrectPath($event->getPath())) {
return;
}

if ($event instanceof CacheEntryRemovedEvent) {
$this->manager->clearMetadata($event->getFileId());
}

if ($event instanceof NodeWrittenEvent) {
$node = $event->getNode();
if ($this->shouldExtractMetadata($node)) {
/** @var File $node */
$this->manager->generateMetadata($event->getNode(), false);
if ($event instanceof CacheEntryInsertedEvent || $event instanceof CacheEntryUpdatedEvent) {
if ($event->getStorage()->filesize($event->getPath()) <= 0) {
return;
}

$owner = $event->getStorage()->getOwner($event->getPath());
$node = $this->rootFolder->getUserFolder($owner)->getById($event->getFileId())[0];

if ($node instanceof File) {
$this->manager->generateMetadata($node, false);
}
}
}
Expand Down

0 comments on commit c3fd7c2

Please sign in to comment.