Files
michaelschiemer/tests/debug/test-advisory-locks.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

260 lines
9.7 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../../src/Framework/Database/Locks/LockKey.php';
require_once __DIR__ . '/../../src/Framework/Database/Locks/AdvisoryLockService.php';
use App\Framework\Database\Locks\LockKey;
use App\Framework\Database\Locks\AdvisoryLockService;
echo "Testing PostgreSQL Advisory Locks\n";
echo "==================================\n\n";
try {
// Create two separate PDO connections to simulate concurrent access
$pdo1 = new PDO(
'pgsql:host=db;dbname=michaelschiemer',
'postgres',
'StartSimple2024!'
);
$pdo1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo2 = new PDO(
'pgsql:host=db;dbname=michaelschiemer',
'postgres',
'StartSimple2024!'
);
$pdo2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "✅ Two database connections established\n\n";
$lockService1 = new AdvisoryLockService($pdo1);
$lockService2 = new AdvisoryLockService($pdo2);
// Test 1: Basic lock acquisition and release
echo "Test 1: Basic Lock Acquisition and Release\n";
echo "===========================================\n";
$key1 = LockKey::fromString('test-resource-1');
echo "Lock key generated: {$key1->toInt()}\n";
// Connection 1 acquires lock
$acquired = $lockService1->tryLock($key1);
echo $acquired ? "✅ Connection 1: Lock acquired\n" : "❌ Connection 1: Failed to acquire lock\n";
// Connection 2 tries to acquire same lock (should fail)
$acquired2 = $lockService2->tryLock($key1);
echo !$acquired2 ? "✅ Connection 2: Lock correctly blocked\n" : "❌ Connection 2: Should not have acquired lock\n";
// Connection 1 releases lock
$released = $lockService1->unlock($key1);
echo $released ? "✅ Connection 1: Lock released\n" : "❌ Connection 1: Failed to release lock\n";
// Connection 2 tries again (should succeed now)
$acquired2 = $lockService2->tryLock($key1);
echo $acquired2 ? "✅ Connection 2: Lock acquired after release\n" : "❌ Connection 2: Failed to acquire lock\n";
$lockService2->unlock($key1);
echo "\n";
// Test 2: Blocking lock behavior
echo "Test 2: Non-Blocking vs Blocking Locks\n";
echo "=======================================\n";
$key2 = LockKey::fromInt(12345);
// Connection 1 acquires lock
$lockService1->tryLock($key2);
echo "✅ Connection 1: Lock acquired (non-blocking)\n";
// Connection 2 tries non-blocking (should fail immediately)
$start = microtime(true);
$acquired2 = $lockService2->tryLock($key2);
$duration = microtime(true) - $start;
echo !$acquired2 ? "✅ Connection 2: tryLock() failed immediately (took " . round($duration * 1000, 2) . "ms)\n" : "❌ Should not acquire lock\n";
$lockService1->unlock($key2);
echo "✅ Connection 1: Lock released\n\n";
// Test 3: Transaction-scoped locks
echo "Test 3: Transaction-Scoped Locks (Auto-Release)\n";
echo "================================================\n";
$key3 = LockKey::fromString('transaction-lock');
// Connection 1 begins transaction and acquires transaction lock
$pdo1->beginTransaction();
$lockService1->lockTransaction($key3);
echo "✅ Connection 1: Transaction lock acquired\n";
// Connection 2 tries to acquire (should fail)
$acquired2 = $lockService2->tryLockTransaction($key3);
echo !$acquired2 ? "✅ Connection 2: Transaction lock correctly blocked\n" : "❌ Should not acquire lock\n";
// Connection 1 commits (auto-releases lock)
$pdo1->commit();
echo "✅ Connection 1: Transaction committed (lock auto-released)\n";
// Connection 2 tries again (should succeed now)
$pdo2->beginTransaction();
$acquired2 = $lockService2->tryLockTransaction($key3);
echo $acquired2 ? "✅ Connection 2: Transaction lock acquired after auto-release\n" : "❌ Failed to acquire lock\n";
$pdo2->commit();
echo "\n";
// Test 4: withLock helper method
echo "Test 4: withLock() Helper Method\n";
echo "=================================\n";
$key4 = LockKey::fromString('helper-lock');
$counter = 0;
$result = $lockService1->withLock($key4, function() use (&$counter) {
$counter++;
echo "✅ Inside withLock() callback: counter = {$counter}\n";
return "operation-result";
});
echo "✅ withLock() returned: {$result}\n";
echo "✅ Counter after operation: {$counter}\n";
// Verify lock was released
$acquired2 = $lockService2->tryLock($key4);
echo $acquired2 ? "✅ Lock was automatically released by withLock()\n" : "❌ Lock should have been released\n";
$lockService2->unlock($key4);
echo "\n";
// Test 5: tryWithLock helper (non-blocking)
echo "Test 5: tryWithLock() Helper (Non-Blocking)\n";
echo "============================================\n";
$key5 = LockKey::fromString('try-helper-lock');
// Connection 1 holds lock
$lockService1->lock($key5);
echo "✅ Connection 1: Lock acquired\n";
// Connection 2 tries with tryWithLock (should return null)
$result2 = $lockService2->tryWithLock($key5, function() {
echo "❌ This callback should NOT execute\n";
return "should-not-happen";
});
echo ($result2 === null) ? "✅ Connection 2: tryWithLock() returned null (lock held by other connection)\n" : "❌ Should have returned null\n";
$lockService1->unlock($key5);
echo "✅ Connection 1: Lock released\n";
// Now Connection 2 tries again (should succeed)
$result2 = $lockService2->tryWithLock($key5, function() {
echo "✅ Inside tryWithLock() callback\n";
return "success";
});
echo ($result2 === "success") ? "✅ Connection 2: tryWithLock() executed and returned result\n" : "❌ Should have executed callback\n";
echo "\n";
// Test 6: Lock key generation methods
echo "Test 6: Lock Key Generation Methods\n";
echo "====================================\n";
$stringKey = LockKey::fromString('my-resource');
echo "✅ String-based key: {$stringKey->toInt()}\n";
$intKey = LockKey::fromInt(999999);
echo "✅ Integer-based key: {$intKey->toInt()}\n";
$pairKey = LockKey::fromPair(100, 200);
echo "✅ Pair-based key: {$pairKey->toInt()}\n";
// Verify same string produces same key (deterministic)
$stringKey2 = LockKey::fromString('my-resource');
echo $stringKey->equals($stringKey2) ? "✅ String keys are deterministic (same string = same key)\n" : "❌ Keys should match\n";
echo "\n";
// Test 7: withTransactionLock helper
echo "Test 7: withTransactionLock() Helper\n";
echo "====================================\n";
$key7 = LockKey::fromString('transaction-helper');
// Create a test table for transaction
$pdo1->exec("DROP TABLE IF EXISTS test_lock_counter");
$pdo1->exec("CREATE TABLE test_lock_counter (value INTEGER)");
$pdo1->exec("INSERT INTO test_lock_counter VALUES (0)");
$result = $lockService1->withTransactionLock($key7, function() use ($pdo1) {
// Read current value
$stmt = $pdo1->query("SELECT value FROM test_lock_counter");
$current = $stmt->fetch(PDO::FETCH_ASSOC)['value'];
// Increment
$pdo1->exec("UPDATE test_lock_counter SET value = " . ($current + 1));
echo "✅ Inside withTransactionLock(): incremented counter from {$current} to " . ($current + 1) . "\n";
return $current + 1;
});
echo "✅ withTransactionLock() committed and returned: {$result}\n";
// Verify transaction was committed
$stmt = $pdo1->query("SELECT value FROM test_lock_counter");
$final = $stmt->fetch(PDO::FETCH_ASSOC)['value'];
echo "✅ Final counter value in database: {$final}\n";
// Cleanup
$pdo1->exec("DROP TABLE test_lock_counter");
echo "\n";
// Test 8: unlockAll() functionality
echo "Test 8: unlockAll() Functionality\n";
echo "==================================\n";
$key8a = LockKey::fromString('multi-lock-1');
$key8b = LockKey::fromString('multi-lock-2');
$key8c = LockKey::fromString('multi-lock-3');
// Acquire multiple locks
$lockService1->lock($key8a);
$lockService1->lock($key8b);
$lockService1->lock($key8c);
echo "✅ Connection 1: Acquired 3 locks\n";
// Verify locks are held
$locked = !$lockService2->tryLock($key8a) && !$lockService2->tryLock($key8b) && !$lockService2->tryLock($key8c);
echo $locked ? "✅ Connection 2: All 3 locks are correctly held by Connection 1\n" : "❌ Locks should be held\n";
// Release all locks at once
$lockService1->unlockAll();
echo "✅ Connection 1: Released all locks with unlockAll()\n";
// Verify all locks are released
$released = $lockService2->tryLock($key8a) && $lockService2->tryLock($key8b) && $lockService2->tryLock($key8c);
echo $released ? "✅ Connection 2: All 3 locks were successfully released\n" : "❌ All locks should be released\n";
$lockService2->unlock($key8a);
$lockService2->unlock($key8b);
$lockService2->unlock($key8c);
echo "\n";
echo "✅ All Advisory Lock tests passed!\n";
echo "\nSummary:\n";
echo "========\n";
echo "✅ Basic lock acquisition and release works\n";
echo "✅ Non-blocking tryLock() works correctly\n";
echo "✅ Transaction-scoped locks auto-release on commit\n";
echo "✅ withLock() helper method works\n";
echo "✅ tryWithLock() non-blocking helper works\n";
echo "✅ Multiple lock key generation methods work\n";
echo "✅ withTransactionLock() helper with auto-commit works\n";
echo "✅ unlockAll() releases multiple locks\n";
} catch (\Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
exit(1);
}