feat: Add comprehensive documentation suite and reorganize project structure
- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
This commit is contained in:
270
f_core/f_workers/queue-monitor.php
Normal file
270
f_core/f_workers/queue-monitor.php
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
// Include core configuration
|
||||
require_once __DIR__ . '/../config.core.php';
|
||||
|
||||
/**
|
||||
* Queue Monitoring and Management Tool
|
||||
*/
|
||||
class QueueMonitor
|
||||
{
|
||||
private $queueManager;
|
||||
private $logger;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->queueManager = new VQueueManager();
|
||||
$this->logger = VLogger::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display queue dashboard
|
||||
*/
|
||||
public function showDashboard()
|
||||
{
|
||||
echo "\n" . str_repeat("=", 80) . "\n";
|
||||
echo " EASYSTREAM QUEUE MONITOR\n";
|
||||
echo str_repeat("=", 80) . "\n\n";
|
||||
|
||||
// Health check
|
||||
$health = $this->queueManager->healthCheck();
|
||||
$this->displayHealthStatus($health);
|
||||
|
||||
// Queue statistics
|
||||
$stats = $this->queueManager->getQueueStatistics();
|
||||
$this->displayQueueStats($stats);
|
||||
|
||||
// Failed jobs
|
||||
$failedJobs = $this->queueManager->getFailedJobs(10);
|
||||
$this->displayFailedJobs($failedJobs);
|
||||
|
||||
echo "\n" . str_repeat("=", 80) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Display health status
|
||||
*/
|
||||
private function displayHealthStatus($health)
|
||||
{
|
||||
echo "SYSTEM HEALTH\n";
|
||||
echo str_repeat("-", 40) . "\n";
|
||||
|
||||
$statusColor = $this->getStatusColor($health['status']);
|
||||
echo "Status: {$statusColor}{$health['status']}\033[0m\n";
|
||||
echo "Backend: {$health['backend']}\n";
|
||||
|
||||
if (!empty($health['issues'])) {
|
||||
echo "Issues:\n";
|
||||
foreach ($health['issues'] as $issue) {
|
||||
echo " - \033[33m{$issue}\033[0m\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Display queue statistics
|
||||
*/
|
||||
private function displayQueueStats($stats)
|
||||
{
|
||||
echo "QUEUE STATISTICS\n";
|
||||
echo str_repeat("-", 40) . "\n";
|
||||
|
||||
if (empty($stats)) {
|
||||
echo "No queue data available\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
// Table header
|
||||
printf("%-15s %-10s %-10s %-10s %-10s\n",
|
||||
"Queue", "Pending", "Processing", "Completed", "Failed");
|
||||
echo str_repeat("-", 65) . "\n";
|
||||
|
||||
foreach ($stats as $queue => $data) {
|
||||
printf("%-15s %-10d %-10d %-10d %-10d\n",
|
||||
$queue,
|
||||
$data['pending'] ?? 0,
|
||||
$data['processing'] ?? 0,
|
||||
$data['completed'] ?? 0,
|
||||
$data['failed'] ?? 0
|
||||
);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Display failed jobs
|
||||
*/
|
||||
private function displayFailedJobs($failedJobs)
|
||||
{
|
||||
echo "RECENT FAILED JOBS\n";
|
||||
echo str_repeat("-", 40) . "\n";
|
||||
|
||||
if (empty($failedJobs)) {
|
||||
echo "No failed jobs found\n\n";
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($failedJobs as $job) {
|
||||
echo "Job ID: {$job['id']}\n";
|
||||
echo "Class: {$job['class']}\n";
|
||||
echo "Queue: {$job['queue']}\n";
|
||||
echo "Attempts: {$job['attempts']}/{$job['max_attempts']}\n";
|
||||
echo "Error: " . substr($job['error_message'] ?? 'Unknown error', 0, 100) . "\n";
|
||||
echo "Failed: {$job['updated_at']}\n";
|
||||
echo str_repeat("-", 40) . "\n";
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old jobs
|
||||
*/
|
||||
public function cleanup($hours = 24)
|
||||
{
|
||||
echo "Cleaning up jobs older than {$hours} hours...\n";
|
||||
|
||||
$deletedCount = $this->queueManager->cleanupOldJobs($hours);
|
||||
|
||||
echo "Cleaned up {$deletedCount} old jobs\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry failed jobs
|
||||
*/
|
||||
public function retryFailedJobs($limit = 10)
|
||||
{
|
||||
echo "Retrying failed jobs (limit: {$limit})...\n";
|
||||
|
||||
$failedJobs = $this->queueManager->getFailedJobs($limit);
|
||||
$retryCount = 0;
|
||||
|
||||
foreach ($failedJobs as $job) {
|
||||
if ($this->queueManager->retryFailedJob($job['id'])) {
|
||||
echo "Retried job: {$job['id']} ({$job['class']})\n";
|
||||
$retryCount++;
|
||||
} else {
|
||||
echo "Failed to retry job: {$job['id']}\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "Retried {$retryCount} jobs\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor queue in real-time
|
||||
*/
|
||||
public function monitor($interval = 5)
|
||||
{
|
||||
echo "Starting real-time queue monitoring (refresh every {$interval}s)\n";
|
||||
echo "Press Ctrl+C to stop\n\n";
|
||||
|
||||
while (true) {
|
||||
// Clear screen
|
||||
system('clear');
|
||||
|
||||
$this->showDashboard();
|
||||
|
||||
echo "Last updated: " . date('Y-m-d H:i:s') . "\n";
|
||||
echo "Refreshing in {$interval} seconds...\n";
|
||||
|
||||
sleep($interval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status color for terminal output
|
||||
*/
|
||||
private function getStatusColor($status)
|
||||
{
|
||||
switch ($status) {
|
||||
case 'healthy':
|
||||
return "\033[32m"; // Green
|
||||
case 'warning':
|
||||
return "\033[33m"; // Yellow
|
||||
case 'unhealthy':
|
||||
return "\033[31m"; // Red
|
||||
default:
|
||||
return "\033[0m"; // Default
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show help information
|
||||
*/
|
||||
public function showHelp()
|
||||
{
|
||||
echo "\nEasyStream Queue Monitor\n";
|
||||
echo str_repeat("=", 30) . "\n\n";
|
||||
echo "Usage: php queue-monitor.php [command] [options]\n\n";
|
||||
echo "Commands:\n";
|
||||
echo " dashboard Show queue dashboard (default)\n";
|
||||
echo " monitor [interval] Real-time monitoring (default: 5s)\n";
|
||||
echo " cleanup [hours] Clean up old jobs (default: 24h)\n";
|
||||
echo " retry [limit] Retry failed jobs (default: 10)\n";
|
||||
echo " help Show this help\n\n";
|
||||
echo "Examples:\n";
|
||||
echo " php queue-monitor.php\n";
|
||||
echo " php queue-monitor.php monitor 10\n";
|
||||
echo " php queue-monitor.php cleanup 48\n";
|
||||
echo " php queue-monitor.php retry 5\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// CLI execution
|
||||
if (php_sapi_name() === 'cli') {
|
||||
$monitor = new QueueMonitor();
|
||||
|
||||
$command = $argv[1] ?? 'dashboard';
|
||||
|
||||
switch ($command) {
|
||||
case 'dashboard':
|
||||
$monitor->showDashboard();
|
||||
break;
|
||||
|
||||
case 'monitor':
|
||||
$interval = (int)($argv[2] ?? 5);
|
||||
$monitor->monitor($interval);
|
||||
break;
|
||||
|
||||
case 'cleanup':
|
||||
$hours = (int)($argv[2] ?? 24);
|
||||
$monitor->cleanup($hours);
|
||||
break;
|
||||
|
||||
case 'retry':
|
||||
$limit = (int)($argv[2] ?? 10);
|
||||
$monitor->retryFailedJobs($limit);
|
||||
break;
|
||||
|
||||
case 'help':
|
||||
case '--help':
|
||||
case '-h':
|
||||
$monitor->showHelp();
|
||||
break;
|
||||
|
||||
default:
|
||||
echo "Unknown command: {$command}\n";
|
||||
$monitor->showHelp();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
?>
|
||||
220
f_core/f_workers/queue-worker.php
Normal file
220
f_core/f_workers/queue-worker.php
Normal file
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
/*******************************************************************************************************************
|
||||
| Software Name : EasyStream
|
||||
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
|
||||
| Software Author : (c) Sami Ahmed
|
||||
|*******************************************************************************************************************
|
||||
|
|
||||
|*******************************************************************************************************************
|
||||
| This source file is subject to the EasyStream Proprietary License Agreement.
|
||||
|
|
||||
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|
||||
|*******************************************************************************************************************
|
||||
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|*******************************************************************************************************************/
|
||||
|
||||
define('_ISVALID', true);
|
||||
|
||||
// Include core configuration
|
||||
require_once __DIR__ . '/../config.core.php';
|
||||
|
||||
/**
|
||||
* Queue Worker for Background Job Processing
|
||||
*/
|
||||
class QueueWorker
|
||||
{
|
||||
private $queue;
|
||||
private $logger;
|
||||
private $running = true;
|
||||
private $maxJobs = 100; // Maximum jobs to process before restarting
|
||||
private $jobsProcessed = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->queue = new VQueue();
|
||||
$this->logger = VLogger::getInstance();
|
||||
|
||||
// Handle shutdown signals gracefully
|
||||
if (function_exists('pcntl_signal')) {
|
||||
pcntl_signal(SIGTERM, [$this, 'shutdown']);
|
||||
pcntl_signal(SIGINT, [$this, 'shutdown']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start processing jobs from specified queues
|
||||
* @param array $queues Queue names to process
|
||||
*/
|
||||
public function work($queues = ['default', 'video_processing', 'notifications'])
|
||||
{
|
||||
$this->logger->info('Queue worker started', [
|
||||
'queues' => $queues,
|
||||
'pid' => getmypid()
|
||||
]);
|
||||
|
||||
while ($this->running && $this->jobsProcessed < $this->maxJobs) {
|
||||
try {
|
||||
// Check for signals
|
||||
if (function_exists('pcntl_signal_dispatch')) {
|
||||
pcntl_signal_dispatch();
|
||||
}
|
||||
|
||||
// Get next job
|
||||
$job = $this->queue->dequeue($queues, 10); // 10 second timeout
|
||||
|
||||
if ($job) {
|
||||
$this->processJob($job);
|
||||
$this->jobsProcessed++;
|
||||
} else {
|
||||
// No jobs available, sleep briefly
|
||||
usleep(100000); // 0.1 seconds
|
||||
}
|
||||
|
||||
// Memory cleanup
|
||||
if ($this->jobsProcessed % 10 === 0) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('Queue worker error', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
// Sleep on error to prevent rapid error loops
|
||||
sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->info('Queue worker stopped', [
|
||||
'jobs_processed' => $this->jobsProcessed,
|
||||
'reason' => $this->running ? 'max_jobs_reached' : 'shutdown_signal'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single job
|
||||
* @param array $job Job data
|
||||
*/
|
||||
private function processJob($job)
|
||||
{
|
||||
$startTime = microtime(true);
|
||||
|
||||
$this->logger->info('Processing job', [
|
||||
'job_id' => $job['id'],
|
||||
'class' => $job['class'],
|
||||
'queue' => $job['queue'],
|
||||
'attempt' => $job['attempts']
|
||||
]);
|
||||
|
||||
try {
|
||||
// Load job class
|
||||
$jobClass = $job['class'];
|
||||
|
||||
if (!$this->loadJobClass($jobClass)) {
|
||||
throw new Exception("Job class {$jobClass} not found");
|
||||
}
|
||||
|
||||
// Create job instance and execute
|
||||
$jobInstance = new $jobClass();
|
||||
|
||||
if (!method_exists($jobInstance, 'handle')) {
|
||||
throw new Exception("Job class {$jobClass} must have a handle method");
|
||||
}
|
||||
|
||||
// Execute job
|
||||
$result = $jobInstance->handle($job['data']);
|
||||
|
||||
// Mark job as completed
|
||||
$this->queue->markCompleted($job['id'], $result);
|
||||
|
||||
$processingTime = microtime(true) - $startTime;
|
||||
|
||||
$this->logger->info('Job completed successfully', [
|
||||
'job_id' => $job['id'],
|
||||
'class' => $job['class'],
|
||||
'processing_time' => $processingTime,
|
||||
'memory_usage' => memory_get_usage(true)
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$processingTime = microtime(true) - $startTime;
|
||||
|
||||
$this->logger->error('Job failed', [
|
||||
'job_id' => $job['id'],
|
||||
'class' => $job['class'],
|
||||
'error' => $e->getMessage(),
|
||||
'processing_time' => $processingTime,
|
||||
'attempt' => $job['attempts']
|
||||
]);
|
||||
|
||||
// Mark job as failed (queue system will handle retries)
|
||||
$this->queue->markFailed($job['id'], $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown handler
|
||||
*/
|
||||
public function shutdown()
|
||||
{
|
||||
$this->logger->info('Queue worker shutdown signal received');
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load a queue job class from supported directories
|
||||
* @param string $jobClass
|
||||
* @return bool
|
||||
*/
|
||||
private function loadJobClass($jobClass)
|
||||
{
|
||||
if (class_exists($jobClass)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$rootDir = dirname(__DIR__, 2);
|
||||
$searchDirectories = [
|
||||
__DIR__,
|
||||
$rootDir . DIRECTORY_SEPARATOR . 'f_jobs',
|
||||
$rootDir . DIRECTORY_SEPARATOR . 'f_core' . DIRECTORY_SEPARATOR . 'f_workers',
|
||||
];
|
||||
|
||||
foreach ($searchDirectories as $directory) {
|
||||
$jobFile = rtrim($directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $jobClass . '.php';
|
||||
if (!is_file($jobFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
require_once $jobFile;
|
||||
|
||||
if (class_exists($jobClass)) {
|
||||
if (method_exists($this->logger, 'debug')) {
|
||||
$this->logger->debug('Job class loaded', [
|
||||
'class' => $jobClass,
|
||||
'path' => $jobFile
|
||||
]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return class_exists($jobClass);
|
||||
}
|
||||
}
|
||||
|
||||
// CLI execution
|
||||
if (php_sapi_name() === 'cli') {
|
||||
$worker = new QueueWorker();
|
||||
|
||||
// Get queues from command line arguments
|
||||
$queues = array_slice($argv, 1);
|
||||
if (empty($queues)) {
|
||||
$queues = ['default', 'video_processing', 'notifications'];
|
||||
}
|
||||
|
||||
echo "Starting queue worker for queues: " . implode(', ', $queues) . "\n";
|
||||
echo "Press Ctrl+C to stop\n\n";
|
||||
|
||||
$worker->work($queues);
|
||||
}
|
||||
Reference in New Issue
Block a user