Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Insert multiple records from single event in chunks #149

Merged
merged 6 commits into from
Nov 2, 2023
Merged
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
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"minimum-stability": "dev",
"require": {
"oat-sa/oatbox-extension-installer": "~1.1||dev-master",
"oat-sa/generis" : ">=15.22",
"oat-sa/tao-core" : ">=50.24.6",
"oat-sa/generis": ">=15.32.0",
"oat-sa/tao-core": ">=53.11.4",
"oat-sa/extension-tao-funcacl" : ">=7.0.0",
"oat-sa/extension-tao-delivery-rdf" : ">=14.0.0",
"oat-sa/extension-tao-item" : ">=11.0.0",
Expand Down
47 changes: 47 additions & 0 deletions migrations/Version202310191436392752_taoEventLog.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace oat\taoEventLog\migrations;

use common_ext_ExtensionsManager;
use Doctrine\DBAL\Schema\Schema;
use oat\oatbox\event\EventManager;
use oat\tao\scripts\tools\migrations\AbstractMigration;
use oat\taoEventLog\model\eventLog\LoggerService;

final class Version202310191436392752_taoEventLog extends AbstractMigration
{
public function getDescription(): string
{
return 'Attach DacChangedEvent to LoggerService';
}

public function up(Schema $schema): void
{
if ($this->getExtensionsManager()->isEnabled('taoDacSimple')) {
$this->getEventManager()->attach(
'oat\taoDacSimple\model\event\DacChangedEvent',
[LoggerService::class, 'log']
);
}
}

public function down(Schema $schema): void
{
$this->getEventManager()->detach(
'oat\taoDacSimple\model\event\DacChangedEvent',
[LoggerService::class, 'log']
);
}

private function getExtensionsManager(): common_ext_ExtensionsManager
{
return $this->getServiceLocator()->get(common_ext_ExtensionsManager::SERVICE_ID);
}

private function getEventManager(): EventManager
{
return $this->getServiceLocator()->get(EventManager::SERVICE_ID);
}
}
2 changes: 2 additions & 0 deletions model/StorageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ interface StorageInterface
*/
public function log(LogEntity $logEntity);

public function logMultiple(LogEntity ...$logEntities): bool;

/**
* Search records in log which are meet the search criteria
*
Expand Down
57 changes: 40 additions & 17 deletions model/eventLog/LoggerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@
use common_exception_Error;
use common_session_Session;
use common_session_SessionManager;
use common_user_User;
use Context;
use DateTimeImmutable;
use DateTimeZone;
use JsonSerializable;
use oat\dtms\DateInterval;
use oat\oatbox\event\BulkEvent;
use oat\oatbox\event\Event;
use oat\oatbox\service\ServiceManager;
use oat\oatbox\user\User;
use oat\taoEventLog\model\storage\RdsStorage as DeprecatedRdsStorage;
use oat\dtms\DateTime;
use oat\taoEventLog\model\AbstractLog;
Expand Down Expand Up @@ -75,24 +77,26 @@ public function setAction($action = '')
*/
public function log(Event $event)
{
/** @var common_session_Session $session */
$session = common_session_SessionManager::getSession();

/** @var common_user_User $currentUser */
$currentUser = $session->getUser();

$data = is_subclass_of($event, JsonSerializable::class) ? $event : [];

$logEntity = new EventLogEntity(
$event,
$this->getAction(),
$currentUser,
(new DateTime('now', new \DateTimeZone('UTC'))),
$data
);
$currentUser = $this->getUser();

try {
$this->getStorage()->log($logEntity);
if ($event instanceof BulkEvent) {
$this->getStorage()->logMultiple(
...array_map(
fn (array $eventData): EventLogEntity => $this->createEventLogEntity(
$event,
$currentUser,
$eventData
),
$event->getValues()
)
);

return;
}

$data = is_subclass_of($event, JsonSerializable::class) ? $event : [];
$this->getStorage()->log($this->createEventLogEntity($event, $currentUser, $data));
} catch (\Exception $e) {
\common_Logger::e('Error logging to DB ' . $e->getMessage());
}
Expand Down Expand Up @@ -137,4 +141,23 @@ protected function getStorage()
$storage = $this->getServiceManager()->get(self::SERVICE_ID)->getOption(self::OPTION_STORAGE);
return $this->getServiceManager()->get($storage);
}

private function getUser(): User
{
/** @var common_session_Session $session */
$session = common_session_SessionManager::getSession();

return $session->getUser();
}

private function createEventLogEntity(Event $event, User $user, $data): EventLogEntity
{
return new EventLogEntity(
$event,
$this->getAction(),
$user,
(new DateTime('now', new DateTimeZone('UTC'))),
$data
);
}
}
53 changes: 53 additions & 0 deletions model/eventLog/RdsStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use oat\taoEventLog\model\LogEntity;
use Doctrine\DBAL\Schema\SchemaException;
use oat\taoEventLog\model\storage\AbstractRdsStorage;
use Throwable;

/**
* Class RdsStorage
Expand All @@ -36,6 +37,8 @@ class RdsStorage extends AbstractRdsStorage

public const SERVICE_ID = 'taoEventLog/eventLogStorage';

public const OPTION_INSERT_CHUNK_SIZE = 'insertChunkSize';

public const EVENT_LOG_ID = self::ID;
public const EVENT_LOG_EVENT_NAME = 'event_name';
public const EVENT_LOG_ACTION = 'action';
Expand All @@ -44,6 +47,8 @@ class RdsStorage extends AbstractRdsStorage
public const EVENT_LOG_OCCURRED = 'occurred';
public const EVENT_LOG_PROPERTIES = 'properties';

private const DEFAULT_INSERT_CHUNK_SIZE = 100;

/**
* @return string
*/
Expand Down Expand Up @@ -73,6 +78,49 @@ public function log(LogEntity $logEntity)
return $result === 1;
}

public function logMultiple(LogEntity ...$logEntities): bool
{
$inserts = array_map(
static fn (LogEntity $logEntity): array => [
self::EVENT_LOG_EVENT_NAME => $logEntity->getEvent()->getName(),
self::EVENT_LOG_ACTION => $logEntity->getAction(),
self::EVENT_LOG_USER_ID => $logEntity->getUser()->getIdentifier(),
self::EVENT_LOG_USER_ROLES => implode(',', $logEntity->getUser()->getRoles()),
self::EVENT_LOG_OCCURRED => $logEntity->getTime()->format(self::DATE_TIME_FORMAT),
self::EVENT_LOG_PROPERTIES => json_encode($logEntity->getData()),
],
$logEntities
);

try {
$persistence = $this->getPersistence();

$persistence->transactional(function () use ($inserts, $persistence) {
$insertCount = count($inserts);
$insertChunkSize = $this->getInsertChunkSize();

foreach (array_chunk($inserts, $insertChunkSize) as $index => $chunk) {
$this->logDebug(
sprintf(
'Processing chunk %d/%d with %d log entries',
$index + 1,
ceil($insertCount / $insertChunkSize),
count($chunk)
)
);

$persistence->insertMultiple($this->getTableName(), $chunk);
}
});

return true;
} catch (Throwable $exception) {
$this->logError('Error when inserting log entries: ' . $exception->getMessage());

return false;
}
}

/**
* @param array $params
* @deprecated use $this->search() instead
Expand Down Expand Up @@ -161,4 +209,9 @@ public static function install($persistence)
$persistence->exec($query);
}
}

private function getInsertChunkSize(): int
{
return $this->getOption(self::OPTION_INSERT_CHUNK_SIZE, self::DEFAULT_INSERT_CHUNK_SIZE);
}
}
5 changes: 5 additions & 0 deletions model/storage/RdsStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public function log(LogEntity $logEntity)
{
}

public function logMultiple(LogEntity ...$logEntities): bool
{
return true;
}

public static function tableColumns()
{
}
Expand Down
4 changes: 4 additions & 0 deletions scripts/install/RegisterLoggerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ public function __invoke($params)
'oat\\taoDacSimple\\model\\event\\DacRemovedEvent',
[LoggerService::class, 'logEvent']
);
$this->registerEvent(
'oat\taoDacSimple\model\event\DacChangedEvent',
[LoggerService::class, 'log']
);
}

if ($extensionManager->isEnabled('taoTestTaker')) {
Expand Down
66 changes: 53 additions & 13 deletions test/model/storage/RdsStorageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2017 (original work) Open Assessment Technologies SA;
*
*
* Copyright (c) 2017-2023 (original work) Open Assessment Technologies SA;
*/

namespace oat\taoEventLog\test\model\requestLog\rds;

use oat\oatbox\log\LoggerService;
use oat\tao\test\TaoPhpUnitTestRunner;
use oat\taoEventLog\model\eventLog\RdsStorage;
use oat\oatbox\service\ServiceManager;
Expand All @@ -32,13 +31,11 @@
use oat\taoEventLog\scripts\install\RegisterRdsStorage;

/**
* Class RdsStorageTest
* @package oat\taoEventLog\test\model\requestLog\rds
* @author Aleh Hutnikau, <[email protected]>
*/
class RdsStorageTest extends TaoPhpUnitTestRunner
{
public function testCount()
public function testCount(): void
{
$storage = $this->getService();
$this->assertEquals(60, $storage->count());
Expand All @@ -49,7 +46,7 @@ public function testCount()
$this->assertEquals(11, count($result));
}

public function testSearch()
public function testSearch(): void
{
$storage = $this->getService();
$this->assertEquals(60, count($storage->search()));
Expand Down Expand Up @@ -96,10 +93,52 @@ public function testSearch()
$this->assertEquals('test_event_10', $result[0][RdsStorage::EVENT_LOG_EVENT_NAME]);
}

/**
* @return RdsStorage
*/
protected function getService()
public function testLogMultiple(): void
{
$storage = $this->getService();

$event = $this->createMock(Event::class);
$event->expects($this->any())
->method('getName')
->willReturn('testEvent');

$user = $this->createMock(User::class);
$user->expects($this->any())
->method('getRoles')
->willReturn([]);

$entities = [
new LogEntity(
$event,
'action1',
$user,
new DateTime()
),
new LogEntity(
$event,
'action2',
$user,
new DateTime()
)
];

$this->assertTrue($storage->logMultiple(...$entities));

$result = $storage->search(
[
[
RdsStorage::EVENT_LOG_EVENT_NAME, '=', 'testEvent']
],
[
'sort' => RdsStorage::EVENT_LOG_OCCURRED,
'order' => 'ASC'
]
);

$this->assertEquals(2, count($result));
}

private function getService(): RdsStorage
{
$persistenceManager = $this->getSqlMock('test_eventlog');
(new RegisterRdsStorage())->createTable($persistenceManager->getPersistenceById('test_eventlog'));
Expand All @@ -109,20 +148,21 @@ protected function getService()
$config = new \common_persistence_KeyValuePersistence([], new \common_persistence_InMemoryKvDriver());
$config->set(\common_persistence_Manager::SERVICE_ID, $persistenceManager);
$serviceManager = new ServiceManager($config);
$serviceManager->register(LoggerService::SERVICE_ID, $this->createMock(LoggerService::class));
$storage->setServiceManager($serviceManager);
$this->loadFixtures($storage);
return $storage;
}

protected function loadFixtures(RdsStorage $storage)
private function loadFixtures(RdsStorage $storage): void
{
for ($i = 0; $i < 60; $i++) {
$eventProphecy = $this->prophesize(Event::class);
$eventProphecy->getName()->willReturn('test_event_' . $i);

$userProphecy = $this->prophesize(User::class);
$userProphecy->getIdentifier()->willReturn('test_user_' . $i);
$userProphecy->getRoles()->willReturn(['role_' . (($i % 5) + 1) , 'role_2' . (($i % 5) + 2)]);
$userProphecy->getRoles()->willReturn(['role_' . (($i % 5) + 1), 'role_2' . (($i % 5) + 2)]);

$logEntity = new LogEntity(
$eventProphecy->reveal(),
Expand Down
Loading