Transaction_historyTransaction History table stats
Transaction History Schema
CREATE TABLE `transaction_history` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`amount` decimal(19,6) NOT NULL,
`description` varchar(150) COLLATE utf8_unicode_ci DEFAULT NULL,
`transaction_type` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'blast',
`remaining_balance` decimal(19,6) NOT NULL,
`created_at` timestamp NOT NULL DEFAULT current_timestamp(),
`updated_at` timestamp NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`,`created_at`),
KEY `transaction_type` (`transaction_type`),
KEY `user_id_transaction_type` (`user_id`,`transaction_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
/**<?php
* Creates the backup for the specified table and the rangenamespace columnconsole\controllers;
* by creating a partition and then exchanging the old data
* partition with the backup table and then move the data touse theYii;
* archiveuse databaseDateTime;
use *Exception;
* @param string $tableName the name of the table to backup data from liveuse DBDateInterval;
* @param string $rangeColumn the name of the column used for the rangeuse partitionyii\helpers\Console;
use *yii\console\Controller;
* @returnuse nullyii\helpers\ArrayHelper;
use */console\controllers\traits\ArchiveTraits;
protected function partitionNow($tableName,
class $rangeColumnPartitionController =extends 'created_at')Controller
{
$rangeOldPartitionuse =ArchiveTraits;
$this->rangeOldPartition(); /**
$backupTableName * Contains array of tables and their range column to be used for partition
* in the key=>value format like [table_name=>range_column_name]
*
* Note: before you add any new table to the below list make sure you have
* added the range column to unique index or part of the primary key
*/
const BACKUP_TABLES = $this->generateBackupTableName($tableName);[
$dbLive 'message_inbound' => 'created_at',
'transaction_history' => 'created_at',
'sms_api_log' => 'created_at',
'balance_history' => 'created_on',
'buy_number_logs' => 'created_at',
'destination_delivery' => 'created_at',
'message_delivery' => 'created_at',
'email_delivery_logs' => 'created_at',
'responder_keywords_logs' => 'created_at',
'sms_alert_logs' => 'created_at',
'suppression_message_logs' => 'created_at',
];
private $_date = self::_getDsnAttribute('dbname');null;
//drop**
backup table if exists * @var batch size for the
$this->dropBackupTables($tableName); * inserts in the database
*/
const BATCH_SIZE = 10000;
$this->stdout("Started/**
Partitioning {$tableName}\n"); * @var limit for the rows to be migrated to the
$startTime * database in one iteration
*/
const MIGRATE_LIMIT = $this->start();50000;
try {
public function $sqlactionIndex($date = <<<SQLnull)
{
-$this- create the backup table and remove partitioning from the>_date backup= table$date;
CREATE TABLE$this->startNotification("Partition `{$dbLive}`.{{%{$backupTableName}}}Process LIKEstarted", `{$dbLive}`.{{%{$tableName}}}'partition-start');
-- start partitioning the sourceini_set("memory_limit", table0);
ALTER$this->stdout("Starting TABLEPartition `{$dbLive}`Process.{{%{$tableName}}}\n");
PARTITION BY RANGE(UNIX_TIMESTAMP$this->startPartition({$rangeColumn}));
$this->stdout(
"Completed Partition Process.\n");
PARTITION oldPt VALUES LESS$date THAN= (UNIX_TIMESTAMPdate("{$rangeOldPartition}")"Y-m-d"),;
PARTITION activePt VALUES LESS THAN $this->sendSummaryReport(MAXVALUE)
"Partitioning Process Complete for {$date}", "partition-complete", "partition");
SQL;}
/**
$command =* Yii::$app->db->createCommand@param int $start the start timestamp
*/
public function end($sql$start);
{
$command->execute return microtime(true); - $start;
}
public function start()
//necessary to catch{
exceptions or errors when return microtime(true);
}
/**
* Starts the partitioning process for the live DB log tables
*
* @return null
* @throws Exception
*/
using multiple SQL statementsprotected withfunction createcommandstartPartition()
{
while foreach ($commandself::BACKUP_TABLES as $tableName => $rangeColumn) {
try {
$this->pdoStatement>partitionNow($tableName, $rangeColumn);
$this->nextRowSet>stdout("\n");
} catch (Exception $e) {
//leave blank do nothing $this->sendExceptionEmail($tableName, this$e, is"Exception foron Partitioning table", "partition-exception");
$this->stdout("There was an error while trying to archive the sake{$tableName} .\n");
//of multiple sql statements that$this->stdout($e->getMessage() are. run"\n===============\n");
in one go $this->stdout("Continuing to archive the next table.\n");
}
}
}
/**
* Creates the backup for the specified table and the range column
* by creating a partition and then exchanging the old data
* partition with the backup table and then move the data to the
* archive database
*
* @param string $tableName the name of the table to backup data from live DB
* @param string $rangeColumn the name of the column used for the range partition
*
* @return null
*/
protected function partitionNow($tableName, $rangeColumn = 'created_at')
{
$rangeOldPartition = $this->stdout>rangeOldPartition("Partitioned);
table in { $backupTableName = $this->end>generateBackupTableName($startTime$tableName)};
secs.\n", Console $dbLive = self::FG_GREEN_getDsnAttribute('dbname');
//drop backup table if exists
$this->dropBackupTables($tableName);
$this->stdout("Started Partitioning {$tableName}\n");
$startTime = $this->start();
try {
$sql = <<<SQL
-- exchangecreate the partitionbackup withtable and remove partitioning from the backup table
ALTER CREATE TABLE `{$dbLive}`.{{%{$backupTableName}}} LIKE `{$dbLive}`.{{%{$tableName}}};
EXCHANGE PARTITION oldPt WITH -- start partitioning the source table
ALTER TABLE `{$dbLive}`.{{%{$backupTableName$tableName}}}
PARTITION BY RANGE(UNIX_TIMESTAMP({$rangeColumn}))
(
PARTITION oldPt VALUES LESS THAN (UNIX_TIMESTAMP("{$rangeOldPartition}")),
PARTITION activePt VALUES LESS THAN (MAXVALUE)
);
SQL;
$command = Yii::$app->db->createCommand($sql);
$command->execute();
$this->stdout("Completed Exchange partition {$this //necessary to catch exceptions or errors when
// using multiple SQL statements with createcommand
while ($command->end>pdoStatement->nextRowSet($startTime)} secs\n"); {
//leave blank do nothing
}
$this->stdout("Partitioned table in {$this->end($startTime)} secs.\n", Console::FG_GREEN);
$startTime = $this->start();
$sql = <<<SQL
-- exchange the partition with the backup table
ALTER TABLE `{$dbLive}`.{{%{$tableName}}}
EXCHANGE PARTITION oldPt WITH TABLE `{$dbLive}`.{{%{$backupTableName}}};
SQL;
$command = Yii::$app->db->createCommand($sql);
$command->execute();
$this->stdout("Completed Exchange partition {$this->end($startTime)} secs\n");
$startTime = $this->start();
$sql = <<<SQL
-- remove partition from the source table once data moved to separate table
ALTER TABLE `{$dbLive}`.{{%{$tableName}}} REMOVE PARTITIONING;
SQL;
$command = Yii::$app->db->createCommand($sql);
$command->execute();
$this->stdout("Removed Partition in {$this->end($startTime)} secs.\n");
$this->stdout("Filterd out data from live table.\n");
} catch (Exception $e) {
throw $e;
}
}
/**
* Takes the source table name ad
*
* @param $tableName
*/
protected function dropBackupTables($tableName)
{
$backupTableName = $this->generateBackupTableName($tableName);
$sql = <<<SQL
-- remove partition from the source table once data moved to separate table
DROP TABLE ALTERIF TABLEEXISTS `{$dbLive}`.{{%{$tableName$backupTableName}}} REMOVE PARTITIONING;;
SQL;
$command = Yii::$app->db->createCommand($sql)->execute();
$command->execute();}
/**
$this->stdout * Generates the backup table name from the source table name
*
* @param string $tableName the source table to mock the backup table from
*
* @return string $backupTableName the name of the backup table
*/
protected function generateBackupTableName("Removed$tableName)
Partition in {$this->end
$backupTableAlias = 'bkp_' . date($startTime'Ymd')} secs.\n"); '_';
$this->stdout("Filterdreturn out"{$backupTableAlias}{$tableName}";
data from live table.\n");}
}/**
catch (Exception $e * Returns the create table command for the given table
* @param $tableName
*/
protected function getCreateTable($tableName)
{
throw$data $e;= Yii::$app->db->createCommand("show create table {{%{$tableName}}}")->queryOne();
return str_replace('CREATE TABLE', 'CREATE TABLE IF NOT EXISTS', $data['Create Table']);
}
}
/**
* Returns the date for the* specified@param interval$tableName
* to backup default interval is 45 days.
*/
* @return mixed
*/
protected function rangeOldPartitiongetPageData()
{
$tableName, $offset = 0)
$date = new DateTime();{
$date->sub(new DateInterval('P0M45D'));
$limit return= $date->format("Y-m-d");
}self::MIGRATE_LIMIT;
/**
* Returns the database name after extracting the
* specific string from the$sql Dsn= property<<<SQL
*
* @param string $name the name of the property in dsn string.
SELECT * @param string $target the target connection of the database live|archive, defaultFROM "live"{{%{$tableName}}}
*
* @return mixed
*/
private static function _getDsnAttribute($name, $target = 'live')
{
order by ifid
($target === 'live') {
ifLIMIT (preg_match("/{$name$offset}=([^;]*)/", Yii::$app->getDb()->dsn, $match)) {
return $match[1];$limit}
}
} else {SQL;
if (preg_match("/{$name}=([^;]*)/",return Yii::$app->db_backup>db->dsn, $match>createCommand($sql)->queryAll() {
return $match[1];
};
}
throw new Exception("Unable to extract the db Name");
}
/**
* Takes the source/**
table name ad
*
* @param $tableName
*/
protected function dropBackupTablesgetRowCount($tableName)
{
$backupTableName $sql = <<<SQL
SELECT COUNT(*) as total FROM {{%{$tableName}}}
SQL;
$data = Yii::$app->db->createCommand($sql)->queryOne();
$this->generateBackupTableName>stdout($tableName"Found {$data['total']} records.");
return $data['total'];
}
/**
* Returns the columns names for a table
*
* @param string $db the database name
* @param string $tableName the table name
*
* @return mixed
*/
protected function getTableColumns($db, $tableName)
{
$sql = <<<SQL
DROPselect
TABLE IF EXISTS {{% COLUMN_NAME
from
information_schema.columns
where
table_schema = "{$backupTableName}$db}" and table_name="{$tableName};"
SQL; order by
Yii::$app->db->createCommand($sql)->execute(); table_name,ordinal_position;
} SQL;
return ArrayHelper::getColumn(
Yii::$app->db->createCommand($sql)->queryAll(),
'COLUMN_NAME'
);
}
/**
* GeneratesReturns the date for the specified interval
* to backup tabledefault nameinterval fromis the45 sourcedays.
table name *
* @return mixed
*/
@param string $tableName protected function rangeOldPartition()
{
$date = new DateTime();
$date->sub(new DateInterval('P0M45D'));
return $date->format("Y-m-d");
}
/**
* Returns the sourcedatabase tablename toafter mockextracting the
backup table * specific string from the Dsn property
*
* @return@param string $backupTableName$name the name of the backupproperty tablein dsn string.
* @param string $target the target connection of the database live|archive, default "live"
*
* @return mixed
*/
protected private static function generateBackupTableName_getDsnAttribute($tableName$name, $target = 'live')
{
$backupTableAlias = 'bkp_' . dateif ('Ymd'$target === 'live') .{
'_'; if (preg_match("/{$name}=([^;]*)/", Yii::$app->getDb()->dsn, $match)) {
return $match[1];
}
} else {
if (preg_match("/{$backupTableAlias$name}=([^;]*)/", Yii::$app->db_backup->dsn, $match)) {$tableName
return $match[1];
}";
}
throw new Exception("Unable to extract the db Name");
}
}