Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
@@ -1,22 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Queue;
|
||||
|
||||
use App\Framework\Cache\Serializer;
|
||||
use App\Framework\Cache\Serializer\PhpSerializer;
|
||||
use App\Framework\Logging\DefaultLogger;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Logging\ProcessorManager;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
use App\Framework\Serializer\Serializer;
|
||||
|
||||
final readonly class FileQueue implements Queue
|
||||
{
|
||||
private string $queueDir;
|
||||
|
||||
private Logger $logger;
|
||||
|
||||
public function __construct(
|
||||
string $queueDir,
|
||||
private Serializer $serializer = new PhpSerializer())
|
||||
{
|
||||
private Serializer $serializer = new PhpSerializer(),
|
||||
?Logger $logger = null
|
||||
) {
|
||||
$this->queueDir = $queueDir;
|
||||
if (!is_dir($queueDir)) {
|
||||
if (! is_dir($queueDir)) {
|
||||
mkdir($queueDir, 0777, true);
|
||||
}
|
||||
|
||||
// Use provided logger or create a null logger for production
|
||||
$this->logger = $logger ?? new DefaultLogger(
|
||||
minLevel: LogLevel::WARNING,
|
||||
handlers: [],
|
||||
processorManager: new ProcessorManager()
|
||||
);
|
||||
}
|
||||
|
||||
public function push(object $job): void
|
||||
@@ -25,46 +41,32 @@ final readonly class FileQueue implements Queue
|
||||
$jobHash = md5($this->serializer->serialize($job));
|
||||
$hashFile = $this->queueDir . '/hash_' . $jobHash . '.job';
|
||||
|
||||
// DETAILED DEBUG: Wer ruft push() auf?
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
|
||||
$caller = '';
|
||||
foreach ($trace as $frame) {
|
||||
if (isset($frame['file']) && isset($frame['line'])) {
|
||||
$caller .= basename($frame['file']) . ':' . $frame['line'] . ' -> ';
|
||||
}
|
||||
}
|
||||
|
||||
error_log("FileQueue Debug: PUSH called from: " . $caller);
|
||||
error_log("FileQueue Debug: Job class: " . get_class($job));
|
||||
error_log("FileQueue Debug: Job data: " . json_encode($job));
|
||||
error_log("FileQueue Debug: Job hash: " . $jobHash);
|
||||
// Debug logging removed for production
|
||||
|
||||
// Prüfe ob identischer Job bereits existiert
|
||||
if (file_exists($hashFile)) {
|
||||
error_log("FileQueue Debug: ⚠️ DUPLICATE JOB REJECTED - hash already exists: " . $jobHash);
|
||||
error_log("FileQueue Debug: Called from: " . $caller);
|
||||
// Silent deduplication - no logging needed in production
|
||||
return; // Job wird nicht hinzugefügt
|
||||
}
|
||||
|
||||
// Verwende Hash als Dateiname für automatische Deduplication
|
||||
file_put_contents($hashFile, $this->serializer->serialize($job));
|
||||
error_log("FileQueue Debug: ✅ Job added with hash: " . $jobHash);
|
||||
// No logging for successful job addition in production
|
||||
}
|
||||
|
||||
public function pop(): ?object
|
||||
{
|
||||
$files = glob($this->queueDir . '/*.job');
|
||||
|
||||
// Debug: Queue-Status loggen
|
||||
// Queue-Status - no logging in production
|
||||
if (empty($files)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
error_log("FileQueue Debug: Found " . count($files) . " job files in " . $this->queueDir);
|
||||
error_log("FileQueue Debug: Files: " . implode(', ', array_map('basename', $files)));
|
||||
// No debug logging for job discovery in production
|
||||
|
||||
// Sortiere nach Erstellungsdatum (FIFO)
|
||||
usort($files, function($a, $b) {
|
||||
usort($files, function ($a, $b) {
|
||||
return filemtime($a) <=> filemtime($b);
|
||||
});
|
||||
|
||||
@@ -73,29 +75,32 @@ final readonly class FileQueue implements Queue
|
||||
|
||||
// Atomic Lock-Mechanismus um race conditions zu verhindern
|
||||
if (file_exists($lockFile)) {
|
||||
error_log("FileQueue Debug: Job file is locked: $file");
|
||||
// Silent handling of locked files - normal operation
|
||||
return null; // Job wird bereits verarbeitet
|
||||
}
|
||||
|
||||
// Lock erstellen
|
||||
if (!@touch($lockFile)) {
|
||||
error_log("FileQueue Debug: Could not create lock file: $lockFile");
|
||||
if (! @touch($lockFile)) {
|
||||
$this->logger->warning("FileQueue: ⚠️ Could not create lock file", ['lock_file' => basename($lockFile)]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Prüfe ob Datei noch existiert (race condition)
|
||||
if (!file_exists($file) || !is_readable($file)) {
|
||||
error_log("FileQueue Debug: Job file not accessible: $file");
|
||||
if (! file_exists($file) || ! is_readable($file)) {
|
||||
$this->logger->warning("FileQueue: ⚠️ Job file not accessible", ['file' => basename($file)]);
|
||||
@unlink($lockFile);
|
||||
|
||||
return $this->pop();
|
||||
}
|
||||
|
||||
$content = file_get_contents($file);
|
||||
if ($content === false) {
|
||||
error_log("FileQueue Debug: Could not read job file: $file");
|
||||
$this->logger->error("FileQueue: ❌ Could not read job file", ['file' => basename($file)]);
|
||||
@unlink($file);
|
||||
@unlink($lockFile);
|
||||
|
||||
return $this->pop();
|
||||
}
|
||||
|
||||
@@ -105,24 +110,29 @@ final readonly class FileQueue implements Queue
|
||||
$tempFile = $file . '.deleting.' . time() . '.' . getmypid();
|
||||
if (rename($file, $tempFile)) {
|
||||
unlink($tempFile);
|
||||
error_log("FileQueue Debug: Successfully deleted job file: " . basename($file));
|
||||
// No logging for successful job completion in production
|
||||
} else {
|
||||
error_log("FileQueue Debug: Failed to rename job file for deletion: $file");
|
||||
$this->logger->warning("FileQueue: ⚠️ Failed to rename job file for deletion", ['file' => basename($file)]);
|
||||
// Fallback: direktes löschen versuchen
|
||||
if (!@unlink($file)) {
|
||||
error_log("FileQueue Debug: Failed to delete job file: $file - moving to processed");
|
||||
if (! @unlink($file)) {
|
||||
$this->logger->warning("FileQueue: ⚠️ Failed to delete job file - moving to processed", ['file' => basename($file)]);
|
||||
$processedFile = $file . '.processed.' . time();
|
||||
@rename($file, $processedFile);
|
||||
}
|
||||
}
|
||||
|
||||
@unlink($lockFile);
|
||||
|
||||
return $job;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
error_log("FileQueue Debug: Failed to process job from file $file: " . $e->getMessage());
|
||||
$this->logger->error("FileQueue: ❌ Failed to process job", [
|
||||
'file' => basename($file),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
@unlink($file);
|
||||
@unlink($lockFile);
|
||||
|
||||
return $this->pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Queue;
|
||||
|
||||
interface Queue
|
||||
{
|
||||
public function push(object $job): void;
|
||||
|
||||
public function pop(): ?object;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Queue;
|
||||
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\Logging\Logger;
|
||||
use App\Framework\Redis\RedisConfig;
|
||||
use App\Framework\Redis\RedisConnection;
|
||||
|
||||
final readonly class QueueInitializer
|
||||
{
|
||||
public function __construct(
|
||||
private PathProvider $pathProvider
|
||||
)
|
||||
{
|
||||
) {
|
||||
}
|
||||
|
||||
#[Initializer]
|
||||
public function __invoke(): Queue
|
||||
public function __invoke(Logger $logger): Queue
|
||||
{
|
||||
$queue = new RedisQueue(
|
||||
queueName: 'commands',
|
||||
host: 'redis',
|
||||
port: 6379,
|
||||
try {
|
||||
$redisConfig = new RedisConfig(
|
||||
host: 'redis',
|
||||
port: 6379,
|
||||
database: 2 // Use DB 2 for queue
|
||||
);
|
||||
$redisConnection = new RedisConnection($redisConfig, 'queue');
|
||||
|
||||
);
|
||||
return new RedisQueue($redisConnection, 'commands');
|
||||
} catch (\Throwable $e) {
|
||||
// Fallback to file queue if Redis is not available
|
||||
$logger->warning("⚠️ Redis queue not available, falling back to file queue", [
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
$path = $this->pathProvider->resolvePath('/src/Framework/CommandBus/storage/queue/');
|
||||
|
||||
$path = $this->pathProvider->resolvePath('/src/Framework/CommandBus/storage/queue/');
|
||||
|
||||
return new FileQueue($path);
|
||||
return new FileQueue($path, logger: $logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Queue;
|
||||
|
||||
use App\Framework\Cache\Serializer;
|
||||
use App\Framework\Cache\Serializer\PhpSerializer;
|
||||
use Predis\Client as Redis;
|
||||
use App\Framework\Redis\RedisConnectionInterface;
|
||||
use App\Framework\Serializer\Php\PhpSerializer;
|
||||
use App\Framework\Serializer\Serializer;
|
||||
use Redis;
|
||||
|
||||
final class RedisQueue implements Queue
|
||||
{
|
||||
private Redis $redis;
|
||||
private string $queueName;
|
||||
|
||||
public function __construct(
|
||||
string $queueName = 'commands',
|
||||
string $host = '127.0.0.1',
|
||||
int $port = 6379,
|
||||
?string $password = null,
|
||||
int $db = 0,
|
||||
private readonly Serializer $serializer = new PhpSerializer
|
||||
private RedisConnectionInterface $connection,
|
||||
private string $queueName = 'commands',
|
||||
private readonly Serializer $serializer = new PhpSerializer()
|
||||
) {
|
||||
$this->queueName = $queueName;
|
||||
$this->redis = new Redis([
|
||||
'scheme' => 'tcp',
|
||||
'host' => $host,
|
||||
'port' => $port,
|
||||
'timeout' => 1.0,
|
||||
'read_write_timeout' => 0,
|
||||
]);
|
||||
$this->redis->connect();
|
||||
if ($password) {
|
||||
$this->redis->auth($password);
|
||||
}
|
||||
$this->redis->select($db);
|
||||
$this->redis = $this->connection->getClient();
|
||||
}
|
||||
|
||||
public function push(object $job): void
|
||||
{
|
||||
$data = $this->serializer->serialize($job);
|
||||
$this->redis->rpush($this->queueName, [$data]);
|
||||
$this->redis->rPush($this->queueName, $data);
|
||||
}
|
||||
|
||||
public function pop(): ?object
|
||||
{
|
||||
// Wartet bis zu 2 Sekunden auf einen neuen Job (blocking)
|
||||
$result = $this->redis->blpop($this->queueName, 2);
|
||||
$result = $this->redis->blPop([$this->queueName], 2);
|
||||
|
||||
if ($result && isset($result[1])) {
|
||||
return $this->serializer->unserialize($result[1]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user