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:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,264 @@
<?php
declare(strict_types=1);
namespace App\Framework\Database\Examples;
use App\Framework\Database\AsyncDatabaseDecorator;
use App\Framework\Database\ConnectionInterface;
/**
* Performance Demo: Sequential vs Parallel Database Operations
*/
final class AsyncDatabaseDemo
{
public static function run(ConnectionInterface $syncDb, AsyncDatabaseDecorator $asyncDb): void
{
echo "🚀 AsyncDatabase Performance Demo\n";
echo "=================================\n\n";
self::demonstrateParallelQueries($syncDb, $asyncDb);
self::demonstrateBatchOperations($asyncDb);
self::demonstrateDataAggregation($asyncDb);
self::demonstrateReplicaQueries($asyncDb);
self::demonstrateOptimisticLocking($asyncDb);
}
private static function demonstrateParallelQueries(ConnectionInterface $syncDb, AsyncDatabaseDecorator $asyncDb): void
{
echo "📊 Test 1: Sequential vs Parallel Database Queries\n";
echo "-" . str_repeat("-", 50) . "\n";
// Setup: Create test queries that simulate different load times
$queries = [
'users' => 'SELECT COUNT(*) as user_count FROM users',
'orders' => 'SELECT COUNT(*) as order_count FROM orders',
'products' => 'SELECT COUNT(*) as product_count FROM products',
'reviews' => 'SELECT COUNT(*) as review_count FROM reviews',
'sessions' => 'SELECT COUNT(*) as session_count FROM user_sessions WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 DAY)',
];
// Sequential execution
echo "⏳ Executing 5 queries sequentially...\n";
$start = microtime(true);
$syncResults = [];
foreach ($queries as $name => $sql) {
try {
$syncResults[$name] = $syncDb->queryScalar($sql);
} catch (\Exception $e) {
$syncResults[$name] = "Error: " . $e->getMessage();
echo "❌ Query '$name' failed: " . $e->getMessage() . "\n";
}
}
$syncTime = microtime(true) - $start;
// Parallel execution
echo "⚡ Executing 5 queries in parallel...\n";
$start = microtime(true);
try {
$queryData = [];
foreach ($queries as $name => $sql) {
$queryData[$name] = ['sql' => $sql, 'method' => 'queryScalar'];
}
$asyncResults = $asyncDb->aggregate($queryData);
} catch (\Exception $e) {
$asyncResults = ["Error: " . $e->getMessage()];
}
$asyncTime = microtime(true) - $start;
$improvement = $syncTime > 0 ? (($syncTime - $asyncTime) / $syncTime) * 100 : 0;
echo "Sequential time: " . number_format($syncTime * 1000, 0) . "ms\n";
echo "Parallel time: " . number_format($asyncTime * 1000, 0) . "ms\n";
echo "Improvement: " . number_format($improvement, 1) . "%\n";
$successCount = 0;
foreach ($asyncResults as $name => $result) {
if ($result['success']) {
$successCount++;
echo "$name: {$result['data']}\n";
} else {
echo "$name: {$result['error']}\n";
}
}
echo "Successful queries: $successCount/" . count($queries) . "\n\n";
}
private static function demonstrateBatchOperations(AsyncDatabaseDecorator $asyncDb): void
{
echo "📦 Test 2: Batch Insert Performance\n";
echo "-" . str_repeat("-", 50) . "\n";
// Generate test data
$testData = [];
for ($i = 1; $i <= 500; $i++) {
$testData[] = [
'name' => "Test User $i",
'email' => "user$i@example.com",
'status' => 'active',
'created_at' => date('Y-m-d H:i:s'),
];
}
echo "Inserting 500 records in batches of 50...\n";
$start = microtime(true);
try {
$results = $asyncDb->batchInsert(
'test_users',
['name', 'email', 'status', 'created_at'],
$testData,
50
);
$time = microtime(true) - $start;
$totalInserted = array_sum($results);
echo "✅ Inserted $totalInserted records in " . number_format($time * 1000, 0) . "ms\n";
echo "Batches completed: " . count($results) . "\n";
echo "Average per batch: " . number_format(($time / count($results)) * 1000, 0) . "ms\n";
} catch (\Exception $e) {
echo "❌ Batch insert failed: " . $e->getMessage() . "\n";
}
echo "\n";
}
private static function demonstrateDataAggregation(AsyncDatabaseDecorator $asyncDb): void
{
echo "📈 Test 3: Dashboard Data Aggregation\n";
echo "-" . str_repeat("-", 50) . "\n";
$start = microtime(true);
try {
$results = $asyncDb->aggregate([
'total_users' => [
'sql' => 'SELECT COUNT(*) FROM users',
'method' => 'queryScalar',
],
'active_sessions' => [
'sql' => 'SELECT COUNT(*) FROM user_sessions WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)',
'method' => 'queryScalar',
],
'recent_orders' => [
'sql' => 'SELECT COUNT(*) as count, COALESCE(SUM(total), 0) as revenue FROM orders WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 DAY)',
'method' => 'queryOne',
],
'top_products' => [
'sql' => 'SELECT p.name, COUNT(*) as sales FROM order_items oi JOIN products p ON oi.product_id = p.id JOIN orders o ON oi.order_id = o.id WHERE o.created_at > DATE_SUB(NOW(), INTERVAL 7 DAYS) GROUP BY p.id ORDER BY sales DESC LIMIT 5',
],
'system_health' => [
'sql' => 'SELECT COUNT(*) as error_count FROM error_logs WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)',
'method' => 'queryScalar',
],
]);
$time = microtime(true) - $start;
echo "Dashboard data loaded in: " . number_format($time * 1000, 0) . "ms\n";
foreach ($results as $metric => $result) {
if ($result['success']) {
if (is_array($result['data'])) {
$count = is_countable($result['data']) ? count($result['data']) : 'N/A';
echo "$metric: $count items loaded\n";
} else {
echo "$metric: {$result['data']}\n";
}
} else {
echo "$metric: {$result['error']}\n";
}
}
} catch (\Exception $e) {
echo "❌ Data aggregation failed: " . $e->getMessage() . "\n";
}
echo "\n";
}
private static function demonstrateReplicaQueries(AsyncDatabaseDecorator $asyncDb): void
{
echo "🔄 Test 4: Replica Query Distribution\n";
echo "-" . str_repeat("-", 50) . "\n";
$sql = 'SELECT COUNT(*) as total FROM users WHERE status = "active"';
echo "Testing replica query distribution...\n";
$start = microtime(true);
try {
$results = $asyncDb->queryReplicas($sql);
$time = microtime(true) - $start;
echo "Query executed on " . count($results) . " replica(s) in " . number_format($time * 1000, 0) . "ms\n";
if (count($results) > 1) {
echo "Results from replicas:\n";
foreach ($results as $i => $result) {
if ($result instanceof \App\Framework\Database\ResultInterface) {
$data = $result->getIterator()->current();
echo " Replica $i: {$data['total']} active users\n";
}
}
} else {
echo "Single connection used (no replicas available)\n";
}
} catch (\Exception $e) {
echo "❌ Replica query failed: " . $e->getMessage() . "\n";
}
echo "\n";
}
private static function demonstrateOptimisticLocking(AsyncDatabaseDecorator $asyncDb): void
{
echo "🔒 Test 5: Optimistic Locking Performance\n";
echo "-" . str_repeat("-", 50) . "\n";
echo "Testing optimistic locking with version control...\n";
// Simulate concurrent updates to the same record
$tests = [
['product_id' => 1, 'reserved' => 5],
['product_id' => 1, 'reserved' => 10], // This should trigger retry
['product_id' => 2, 'reserved' => 3],
];
foreach ($tests as $i => $test) {
$start = microtime(true);
try {
$success = $asyncDb->optimisticUpdate(
'inventory',
['reserved' => $test['reserved']],
['product_id' => $test['product_id']],
'version',
maxRetries: 3
);
$time = microtime(true) - $start;
$status = $success ? "✅ Success" : "❌ Failed";
echo "Test " . ($i + 1) . " (Product {$test['product_id']}): $status - " .
number_format($time * 1000, 0) . "ms\n";
} catch (\Exception $e) {
$time = microtime(true) - $start;
echo "Test " . ($i + 1) . " (Product {$test['product_id']}): ❌ Error - " .
number_format($time * 1000, 0) . "ms - " . $e->getMessage() . "\n";
}
}
echo "\n";
}
}
// Usage example:
// $syncDb = new PdoConnection($pdo);
// $asyncDb = new AsyncDatabaseDecorator($syncDb, $asyncService);
// AsyncDatabaseDemo::run($syncDb, $asyncDb);

View File

@@ -0,0 +1,295 @@
<?php
declare(strict_types=1);
namespace App\Framework\Database\Examples;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Database\AsyncDatabaseDecorator;
/**
* Real-world examples for AsyncDatabaseDecorator
*/
final readonly class AsyncDatabaseExample
{
public function __construct(
private AsyncDatabaseDecorator $db
) {
}
/**
* Example: Dashboard data aggregation
*/
public function loadDashboardData(int $userId): array
{
return $this->db->aggregate([
'user_profile' => [
'sql' => 'SELECT id, name, email, avatar FROM users WHERE id = ?',
'params' => [$userId],
'method' => 'queryOne',
],
'user_stats' => [
'sql' => 'SELECT COUNT(*) as post_count FROM posts WHERE user_id = ?',
'params' => [$userId],
'method' => 'queryScalar',
],
'recent_orders' => [
'sql' => 'SELECT id, total, created_at FROM orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 5',
'params' => [$userId],
],
'unread_notifications' => [
'sql' => 'SELECT COUNT(*) FROM notifications WHERE user_id = ? AND read_at IS NULL',
'params' => [$userId],
'method' => 'queryScalar',
],
'account_balance' => [
'sql' => 'SELECT balance FROM accounts WHERE user_id = ?',
'params' => [$userId],
'method' => 'queryScalar',
],
]);
}
/**
* Example: E-commerce product page data
*/
public function loadProductPageData(int $productId): array
{
return $this->db->aggregate([
'product' => [
'sql' => 'SELECT * FROM products WHERE id = ?',
'params' => [$productId],
'method' => 'queryOne',
],
'inventory' => [
'sql' => 'SELECT quantity, reserved FROM inventory WHERE product_id = ?',
'params' => [$productId],
'method' => 'queryOne',
],
'reviews_summary' => [
'sql' => 'SELECT AVG(rating) as avg_rating, COUNT(*) as review_count FROM reviews WHERE product_id = ?',
'params' => [$productId],
'method' => 'queryOne',
],
'recent_reviews' => [
'sql' => 'SELECT rating, comment, user_name, created_at FROM reviews WHERE product_id = ? ORDER BY created_at DESC LIMIT 5',
'params' => [$productId],
],
'related_products' => [
'sql' => 'SELECT id, name, price, image FROM products WHERE category_id = (SELECT category_id FROM products WHERE id = ?) AND id != ? LIMIT 4',
'params' => [$productId, $productId],
],
'price_history' => [
'sql' => 'SELECT price, created_at FROM price_history WHERE product_id = ? ORDER BY created_at DESC LIMIT 10',
'params' => [$productId],
],
]);
}
/**
* Example: Bulk user import
*/
public function importUsers(array $userData): array
{
$columns = ['name', 'email', 'password_hash', 'created_at'];
// Prepare data
$rows = [];
foreach ($userData as $user) {
$rows[] = [
$user['name'],
$user['email'],
password_hash($user['password'], PASSWORD_DEFAULT),
date('Y-m-d H:i:s'),
];
}
// Insert in batches of 100
return $this->db->batchInsert('users', $columns, $rows, 100);
}
/**
* Example: Bulk order status updates
*/
public function updateOrderStatuses(array $orderUpdates): array
{
// Each update: ['id' => 123, 'status' => 'shipped', 'shipped_at' => '2024-01-01']
return $this->db->batchUpdate('orders', $orderUpdates, 'id', 25);
}
/**
* Example: Analytics data collection
*/
public function collectAnalyticsData(\DateTime $date): array
{
$dateStr = $date->format('Y-m-d');
return $this->db->aggregate([
'daily_users' => [
'sql' => 'SELECT COUNT(DISTINCT user_id) FROM user_sessions WHERE DATE(created_at) = ?',
'params' => [$dateStr],
'method' => 'queryScalar',
],
'daily_orders' => [
'sql' => 'SELECT COUNT(*), SUM(total) as revenue FROM orders WHERE DATE(created_at) = ?',
'params' => [$dateStr],
'method' => 'queryOne',
],
'popular_products' => [
'sql' => 'SELECT p.name, COUNT(*) as sales FROM order_items oi JOIN products p ON oi.product_id = p.id JOIN orders o ON oi.order_id = o.id WHERE DATE(o.created_at) = ? GROUP BY p.id ORDER BY sales DESC LIMIT 10',
'params' => [$dateStr],
],
'top_customers' => [
'sql' => 'SELECT u.name, u.email, SUM(o.total) as spent FROM users u JOIN orders o ON u.id = o.user_id WHERE DATE(o.created_at) = ? GROUP BY u.id ORDER BY spent DESC LIMIT 10',
'params' => [$dateStr],
],
'page_views' => [
'sql' => 'SELECT page, COUNT(*) as views FROM page_views WHERE DATE(created_at) = ? GROUP BY page ORDER BY views DESC LIMIT 20',
'params' => [$dateStr],
],
]);
}
/**
* Example: Database health monitoring
*/
public function monitorDatabaseHealth(): array
{
$tables = ['users', 'orders', 'products', 'order_items', 'reviews'];
$tableStats = $this->db->getTableStats($tables);
$systemStats = $this->db->aggregate([
'active_connections' => [
'sql' => 'SHOW STATUS LIKE "Threads_connected"',
'method' => 'queryOne',
],
'slow_queries' => [
'sql' => 'SHOW STATUS LIKE "Slow_queries"',
'method' => 'queryOne',
],
'uptime' => [
'sql' => 'SHOW STATUS LIKE "Uptime"',
'method' => 'queryOne',
],
'innodb_buffer_pool_usage' => [
'sql' => 'SELECT (1 - (FREE_BUFFERS / TOTAL_BUFFERS)) * 100 as usage_percent FROM (SELECT VARIABLE_VALUE as FREE_BUFFERS FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME = "Innodb_buffer_pool_pages_free") f CROSS JOIN (SELECT VARIABLE_VALUE as TOTAL_BUFFERS FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME = "Innodb_buffer_pool_pages_total") t',
'method' => 'queryScalar',
],
]);
return [
'table_stats' => $tableStats,
'system_stats' => $systemStats,
'timestamp' => time(),
];
}
/**
* Example: User activity timeline
*/
public function getUserActivityTimeline(int $userId, int $days = 30): array
{
$since = date('Y-m-d', strtotime("-$days days"));
return $this->db->aggregate([
'login_history' => [
'sql' => 'SELECT DATE(created_at) as date, COUNT(*) as logins FROM user_sessions WHERE user_id = ? AND created_at >= ? GROUP BY DATE(created_at) ORDER BY date DESC',
'params' => [$userId, $since],
],
'order_history' => [
'sql' => 'SELECT DATE(created_at) as date, COUNT(*) as orders, SUM(total) as spent FROM orders WHERE user_id = ? AND created_at >= ? GROUP BY DATE(created_at) ORDER BY date DESC',
'params' => [$userId, $since],
],
'page_views' => [
'sql' => 'SELECT DATE(created_at) as date, COUNT(*) as views FROM page_views WHERE user_id = ? AND created_at >= ? GROUP BY DATE(created_at) ORDER BY date DESC',
'params' => [$userId, $since],
],
'support_tickets' => [
'sql' => 'SELECT DATE(created_at) as date, COUNT(*) as tickets FROM support_tickets WHERE user_id = ? AND created_at >= ? GROUP BY DATE(created_at) ORDER BY date DESC',
'params' => [$userId, $since],
],
]);
}
/**
* Example: Inventory management with optimistic locking
*/
public function reserveInventory(int $productId, int $quantity): bool
{
return $this->db->optimisticUpdate(
'inventory',
['reserved' => $quantity],
['product_id' => $productId],
'version',
maxRetries: 5
);
}
/**
* Example: Complex reporting with timeout protection
*/
public function generateSalesReport(\DateTime $startDate, \DateTime $endDate): array
{
return $this->db->transactionWithTimeout(function ($db) use ($startDate, $endDate) {
$start = $startDate->format('Y-m-d');
$end = $endDate->format('Y-m-d');
return $db->aggregate([
'summary' => [
'sql' => 'SELECT COUNT(*) as total_orders, SUM(total) as total_revenue, AVG(total) as avg_order_value FROM orders WHERE created_at BETWEEN ? AND ?',
'params' => [$start, $end],
'method' => 'queryOne',
],
'by_day' => [
'sql' => 'SELECT DATE(created_at) as date, COUNT(*) as orders, SUM(total) as revenue FROM orders WHERE created_at BETWEEN ? AND ? GROUP BY DATE(created_at) ORDER BY date',
'params' => [$start, $end],
],
'by_product' => [
'sql' => 'SELECT p.name, SUM(oi.quantity) as units_sold, SUM(oi.price * oi.quantity) as revenue FROM order_items oi JOIN products p ON oi.product_id = p.id JOIN orders o ON oi.order_id = o.id WHERE o.created_at BETWEEN ? AND ? GROUP BY p.id ORDER BY revenue DESC LIMIT 20',
'params' => [$start, $end],
],
'by_customer' => [
'sql' => 'SELECT u.name, u.email, COUNT(o.id) as orders, SUM(o.total) as total_spent FROM users u JOIN orders o ON u.id = o.user_id WHERE o.created_at BETWEEN ? AND ? GROUP BY u.id ORDER BY total_spent DESC LIMIT 20',
'params' => [$start, $end],
],
]);
}, Duration::fromMinutes(5));
}
/**
* Example: Background data cleanup
*/
public function cleanupOldData(): void
{
// Non-blocking cleanup operations
$this->db->executeAsync('DELETE FROM user_sessions WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)');
$this->db->executeAsync('DELETE FROM page_views WHERE created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)');
$this->db->executeAsync('DELETE FROM audit_logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR)');
$this->db->executeAsync('DELETE FROM temporary_uploads WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 DAY)');
}
/**
* Example: Read-ahead caching for related data
*/
public function preloadRelatedData(array $productIds): array
{
$queries = [];
foreach ($productIds as $productId) {
$queries["reviews_$productId"] = [
'sql' => 'SELECT COUNT(*) as count, AVG(rating) as avg_rating FROM reviews WHERE product_id = ?',
'params' => [$productId],
'method' => 'queryOne',
];
$queries["inventory_$productId"] = [
'sql' => 'SELECT quantity FROM inventory WHERE product_id = ?',
'params' => [$productId],
'method' => 'queryScalar',
];
}
return $this->db->readAhead($queries, Duration::fromSeconds(5));
}
}