+namespace App\Console\Commands;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\Cache;
+class PullMaster extends Command
+ * The name and signature of the console command.
+ protected $signature = 'sdb:pull-master'
+ .' {--force : Force process without checking lockfile }'
+ * The console command description.
+ protected $description = 'Sync tables from other database. In env it specified as DB2_*';
+ * Create a new command instance.
+ public function __construct() {
+ $this->cacheFile = Cache::store('file');
+ $this->cmd_name = (new \ReflectionClass($this))->getShortName();
+ // define lockfile to stay in minutes
+ $this->expire_lockfile = 20;
+ // Size of chunked rows
+ $this->chunked_size = 30;
+ $this->overwrite_row = true;
+ $this->key_process = 'sdb:pullmaster-lock';
+ * Execute the console command.
+ public function handle() {
+ if (self::isThereProcess()) {
+ $this->info('There is still process running: '.$this->key_process);
+ $this->info('To allow command run anyway, use option: --force');
+ if($this->expireProcess) {
+ $expCarbon = Carbon::createFromTimestamp($this->expireProcess);
+ $this->info('Try again later: '.$expCarbon->format('d M Y, H:i:s'));
+ $start_time = microtime(true);
+ \Log::info($this->cmd_name.' initiated.');
+ $this->info($this->cmd_name.' started...');
+ $options = $this->options();
+ $params['verbose'] = $options['verbose'];
+ $is_verbose = !empty($params['verbose']);
+ 'task' => '\App\Models\Task',
+ 'tracker' => '\App\Models\Tracker',
+ 'tracker_tasks' => '\App\Models\TrackerTasks',
+ $todos = array_keys($maptables);
+ foreach($todos as $todo){
+ $model_name = (isset($maptables[$todo]) ? $maptables[$todo] : null);
+ if (empty($model_name))
+ $this->line(' > Proceeding: '.$todo);
+ $Model = new $model_name;
+ }catch (Exception $e) {
+ \Log::error('Unable initiating model: '.$model_name);
+ $this->importTable($Model);
+ private function importTable($Model){
+ * Insert bulk of collection rows into its Model's table
+ * @param Eloquent collection results
+ $insertHandler = function($_rows) use ($Model){
+ if (empty($_rows) || (!empty($_rows) && $_rows->isEmpty())){
+ \Log::warn('Empty row');
+ $is_verbose = $this->option('verbose');
+ foreach($_rows as $row){
+ $item_data = json_decode(json_encode($row), true);
+ $existing = $Model->find($row->id);
+ if (!empty($existing)){
+ $this->info(' > Row exists, ID: '.$row->id);
+ if ($this->overwrite_row){
+ $existing->forceDelete();
+ $this->info(' > Skipping..');
+ if ($ret_item = $Model::create($item_data))
+ return $count_inserted;
+ // $fields = ['id', 'track_value'];
+ $items = $Model->setConnection('mysql2')
+ $count_items = $items->count();
+ $chunkSize = $this->chunked_size;
+ $this->info(' > Proceed with chunked data: '.$chunkSize);
+ // Save ur arse, memory preserve
+ $items->chunk($chunkSize, function($rows) use ($insertHandler, &$chunk_step){
+ $this->info(' > Chunk-step #'.$chunk_step);
+ private function isThereProcess() {
+ $runningProcess = null;
+ if($this->option('force'))
+ self::destroyLockFile();
+ $key_process = $this->key_process;
+ $theCache = $this->cacheFile;
+ $lock_value = $theCache->get($key_process);
+ $generateLockFile = function($expireAt=null) use ($theCache, $key_process) {
+ $expireAt = Carbon::now();
+ $expireAt = $expireAt->addMinutes($this->expire_lockfile);
+ $this->info($this->cmd_name.' initiated: '.Carbon::now()->format('d M Y, H:i:s'));
+ $this->info('Lock file created. Expire in '.$this->expire_lockfile.' minutes ('.$expireAt->format('d M Y, H:i:s').')');
+ if (empty($lock_value)) {
+ $generateLockFile($now);
+ $runningProcess = false;
+ if ($now->timestamp > $lock_value) {
+ $generateLockFile($now);
+ $runningProcess = false;
+ $runningProcess = true;
+ $this->expireProcess = $lock_value;
+ return $runningProcess;
+ * Once process is done we destroy lock-file like
+ * to allow another instance to run
+ private function destroyLockFile() {
+ $theCache = $this->cacheFile;
+ if ($theCache->has($this->key_process)) {
+ $this->info('Destroying lockfile');
+ $theCache->forget($this->key_process);