|
|
@@ -5,8 +5,9 @@ namespace Avolver\CompanyEventsKitchen\Repository;
|
|
|
|
|
|
use Avolver\CompanyEventsKitchen\Client\ClickHouseClient;
|
|
|
use Avolver\CompanyEventsKitchen\Event\CompanyEvent;
|
|
|
-use Avolver\CompanyEventsKitchen\Helper\RandomIterator;
|
|
|
use Avolver\CompanyEventsKitchen\Traits\OutputTrait;
|
|
|
+use Avolver\CompanyEventsKitchen\Traits\ProgressBarRepoTrait;
|
|
|
+use ClickHouseDB\DatabaseException;
|
|
|
use MongoDB\BSON\ObjectId;
|
|
|
use MongoDB\Driver\Cursor as MongoCursor;
|
|
|
|
|
|
@@ -19,6 +20,7 @@ class EventsRepository
|
|
|
{
|
|
|
|
|
|
use OutputTrait;
|
|
|
+ use ProgressBarRepoTrait;
|
|
|
|
|
|
/**
|
|
|
* @var ClickHouseClient
|
|
|
@@ -28,12 +30,12 @@ class EventsRepository
|
|
|
/**
|
|
|
* @var \DateTime
|
|
|
*/
|
|
|
- private $startDate;
|
|
|
+ private $startMonth;
|
|
|
|
|
|
/**
|
|
|
- * @var \DateTime
|
|
|
+ * @var int
|
|
|
*/
|
|
|
- private $endDate;
|
|
|
+ private $daysInMonth;
|
|
|
|
|
|
/**
|
|
|
* Имя БД
|
|
|
@@ -60,14 +62,12 @@ class EventsRepository
|
|
|
* EventsRepository constructor.
|
|
|
*
|
|
|
* @param ClickHouseClient $clickhouse
|
|
|
- * @param \DateTime $startDate
|
|
|
- * @param \DateTime $endDate
|
|
|
+ * @param \DateTime $startMonth
|
|
|
*/
|
|
|
- public function __construct(ClickHouseClient $clickhouse, \DateTime $startDate, \DateTime $endDate)
|
|
|
+ public function __construct(ClickHouseClient $clickhouse, \DateTime $startMonth = null)
|
|
|
{
|
|
|
$this->clickhouse = $clickhouse;
|
|
|
- $this->startDate = $startDate;
|
|
|
- $this->endDate = $endDate;
|
|
|
+ $this->startMonth = $startMonth;
|
|
|
$this->databaseName = $this->clickhouse->getConfig()->getDatabase();
|
|
|
$this->tableName = $this->clickhouse->getConfig()->getEventsTable();
|
|
|
}
|
|
|
@@ -79,12 +79,12 @@ class EventsRepository
|
|
|
{
|
|
|
// Создание БД, если это необходимо
|
|
|
$this->clickhouse->write(
|
|
|
- sprintf('CREATE DATABASE IF NOT EXISTS %s', $this->databaseName)
|
|
|
+ \sprintf('CREATE DATABASE IF NOT EXISTS %s', $this->databaseName)
|
|
|
);
|
|
|
|
|
|
$eventTypesMap = [];
|
|
|
foreach (CompanyEvent::toArray() as $key => $value) {
|
|
|
- $eventTypesMap[] = sprintf('\'%s\' = %s', $key, $value);
|
|
|
+ $eventTypesMap[] = \sprintf('\'%s\' = %s', $key, $value);
|
|
|
}
|
|
|
|
|
|
$createTableQuery = <<<QUERY
|
|
|
@@ -92,18 +92,16 @@ class EventsRepository
|
|
|
event_date Date,
|
|
|
event_time DateTime,
|
|
|
event_type Enum8(%s),
|
|
|
- id_1 Int32,
|
|
|
- id_2 Int32,
|
|
|
- id_3 Int32
|
|
|
+ company_id FixedString(24)
|
|
|
)
|
|
|
- ENGINE = MergeTree(event_date, (id_1, id_2, id_3), 8192)
|
|
|
+ ENGINE = MergeTree(event_date, (company_id), 8192)
|
|
|
QUERY;
|
|
|
|
|
|
- $this->clickhouse->write(sprintf(
|
|
|
+ $this->clickhouse->write(\sprintf(
|
|
|
$createTableQuery,
|
|
|
$this->databaseName,
|
|
|
$this->tableName,
|
|
|
- implode(', ', $eventTypesMap)
|
|
|
+ \implode(', ', $eventTypesMap)
|
|
|
));
|
|
|
}
|
|
|
|
|
|
@@ -117,31 +115,40 @@ QUERY;
|
|
|
*/
|
|
|
public function generateEventsForCompanies(MongoCursor $cursor, int $needCount = 3200): void
|
|
|
{
|
|
|
- //$this->createTableIfNeeded();
|
|
|
- $eventTypeCount = \count(CompanyEvent::keys());
|
|
|
+ if (!$this->startMonth) {
|
|
|
+ throw new \InvalidArgumentException('Дата стартового месяца не определена');
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->createTableIfNeeded();
|
|
|
$this->clickhouse->database($this->databaseName);
|
|
|
+ $this->daysInMonth = (int) $this->startMonth->format('t');
|
|
|
+
|
|
|
+ $eventTypeCount = \count(CompanyEvent::keys());
|
|
|
+ $threshold = 10000;
|
|
|
+ $eachCount = 0;
|
|
|
|
|
|
- $threshold = 100000;
|
|
|
- $eachCount = 0;
|
|
|
+ // @todo прочитать количество из MongoDB
|
|
|
+ $progress = $this->createProgressBar($needCount * 1500000);
|
|
|
|
|
|
foreach ($cursor as $company) {
|
|
|
$eventCount = random_int($needCount - 10, $needCount + 10);
|
|
|
$eventCount = $eventCount > 0 ? $eventCount : 10;
|
|
|
for ($count = 1; $count <= $eventCount; $count++) {
|
|
|
- $randomDate = $this->getRandomDate($this->startDate, $this->endDate);
|
|
|
+ $randomDate = $this->getRandomDate();
|
|
|
$randomEventKey = random_int(1, $eventTypeCount);
|
|
|
$this->addEventToQueue($company->_id, $randomDate, new CompanyEvent($randomEventKey));
|
|
|
|
|
|
$eachCount++;
|
|
|
if ($eachCount % $threshold === 0) {
|
|
|
$this->insertEventsFromQueue();
|
|
|
- gc_collect_cycles();
|
|
|
- // echo " + 100k\n";
|
|
|
+ $this->advanceProgressBar($progress, $threshold);
|
|
|
+ \gc_collect_cycles();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
+ $this->finishProgressBar($progress);
|
|
|
+
|
|
|
$this->insertEventsFromQueue();
|
|
|
}
|
|
|
|
|
|
@@ -154,15 +161,11 @@ QUERY;
|
|
|
*/
|
|
|
public function addEventToQueue(ObjectId $companyId, \DateTime $datetime, CompanyEvent $event): void
|
|
|
{
|
|
|
- $idChunks = $this->convertObjectIdToInts($companyId);
|
|
|
-
|
|
|
$this->bulkQueue[] = [
|
|
|
$datetime->format('Y-m-d'),
|
|
|
$datetime->getTimestamp(),
|
|
|
$event->getKey(),
|
|
|
- $idChunks[0],
|
|
|
- $idChunks[1],
|
|
|
- $idChunks[2]
|
|
|
+ (string) $companyId
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -171,58 +174,81 @@ QUERY;
|
|
|
*/
|
|
|
public function insertEventsFromQueue(): void
|
|
|
{
|
|
|
- if (!$this->bulkQueue || \count($this->bulkQueue)) {
|
|
|
+ if (!$this->bulkQueue || !\count($this->bulkQueue)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- $this->clickhouse->insert(
|
|
|
- $this->tableName,
|
|
|
- [
|
|
|
- $this->bulkQueue
|
|
|
- ],
|
|
|
- [
|
|
|
- 'event_date', 'event_time', 'event_type', 'id_1', 'id_2', 'id_3'
|
|
|
- ]
|
|
|
- );
|
|
|
+ // Сортировка событий по времени, которая необходима для быстрой вставки в ClickHouse
|
|
|
+ \usort($this->bulkQueue, function ($left, $right) {
|
|
|
+ return $left[1] > $right[1];
|
|
|
+ });
|
|
|
+
|
|
|
+ try {
|
|
|
+ $this->clickhouse->insert(
|
|
|
+ $this->tableName,
|
|
|
+ $this->bulkQueue,
|
|
|
+ [
|
|
|
+ 'event_date', 'event_time', 'event_type', 'company_id'
|
|
|
+ ]
|
|
|
+ );
|
|
|
+ } catch (DatabaseException $e) {
|
|
|
+ throw $e; // new $e(substr($e->getMessage(), 0, 256) . ' — row ' . print_r($e->));
|
|
|
+ }
|
|
|
+
|
|
|
$this->bulkQueue = [];
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Получение случайной даты из диапозона
|
|
|
+ * Получение массива событий за конкретный месяц
|
|
|
*
|
|
|
- * @param \DateTime $startDate
|
|
|
- * @param \DateTime $endDate
|
|
|
+ * @param ObjectId $companyId
|
|
|
+ * @param int $month
|
|
|
+ * @param int $year
|
|
|
*
|
|
|
- * @return \DateTime
|
|
|
+ * @return array
|
|
|
*
|
|
|
- * @throws \Exception
|
|
|
+ * @todo ...
|
|
|
*/
|
|
|
- public function getRandomDate(\DateTime $startDate, \DateTime $endDate): \DateTime
|
|
|
+ public function findEventsByCompanyId(ObjectId $companyId, int $month, int $year): ?array
|
|
|
{
|
|
|
- $interval = new \DateInterval('P1D');
|
|
|
- $period = new \DatePeriod($startDate, $interval, $endDate);
|
|
|
- $random = new RandomIterator($period);
|
|
|
-
|
|
|
- [$result] = iterator_to_array($random, false) ? : [null];
|
|
|
+ $query = <<<QUERY
|
|
|
+ SELECT
|
|
|
+ event_date AS date,
|
|
|
+ event_type AS type,
|
|
|
+ count() AS count
|
|
|
+ FROM kitchen.company_events
|
|
|
+ WHERE
|
|
|
+ company_id = :company_id
|
|
|
+ AND toMonth(event_date) = :month
|
|
|
+ AND toYear(event_date) = :year
|
|
|
+ GROUP BY event_date, event_type
|
|
|
+ ORDER BY event_date
|
|
|
+QUERY;
|
|
|
|
|
|
- return $result;
|
|
|
+ return $this->clickhouse
|
|
|
+ ->select(
|
|
|
+ $query,
|
|
|
+ [
|
|
|
+ 'company_id' => (string) $companyId,
|
|
|
+ 'month' => $month,
|
|
|
+ 'year' => $year
|
|
|
+ ]
|
|
|
+ )
|
|
|
+ ->rows();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Преобразует идентификатор mongo в три целых числа
|
|
|
+ * Получение случайной даты за текущий диапозона
|
|
|
*
|
|
|
- * @param ObjectId $objectId
|
|
|
+ * @return \DateTime
|
|
|
*
|
|
|
- * @return array[int, int, int]
|
|
|
+ * @throws \Exception
|
|
|
*/
|
|
|
- private function convertObjectIdToInts(ObjectId $objectId): array
|
|
|
+ public function getRandomDate(): \DateTime
|
|
|
{
|
|
|
- $idString = (string) $objectId;
|
|
|
-
|
|
|
- return [
|
|
|
- hexdec(mb_substr($idString, 0, 8)),
|
|
|
- hexdec(mb_substr($idString, 8, 8)),
|
|
|
- hexdec(mb_substr($idString, 16))
|
|
|
- ];
|
|
|
+ return (clone $this->startMonth)
|
|
|
+ ->modify(
|
|
|
+ \sprintf('+ %d day', random_int(0, $this->daysInMonth - 2))
|
|
|
+ );
|
|
|
}
|
|
|
}
|