Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/sentry/src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace FriendsOfHyperf\Sentry;

use Hyperf\Context\Context;

class Constants
{
public const TRACE_CARRIER = 'sentry.tracing.trace_carrier';
Expand All @@ -21,5 +23,15 @@ class Constants

public const TRACEPARENT = 'traceparent';

public static bool $runningInCommand = false;
public const CTX_RUNNING_IN_COMMAND = 'sentry.constants.running_in_command';

public static function runningInCommand(): bool
{
return (bool) Context::get(self::CTX_RUNNING_IN_COMMAND, false);
}

public static function setRunningInCommand(bool $running = true): void
{
Context::set(self::CTX_RUNNING_IN_COMMAND, $running);
}
}
7 changes: 4 additions & 3 deletions src/sentry/src/Integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace FriendsOfHyperf\Sentry;

use Hyperf\Context\Context;
use Sentry\Breadcrumb;
use Sentry\Event;
use Sentry\Integration\IntegrationInterface;
Expand All @@ -28,7 +29,7 @@

class Integration implements IntegrationInterface
{
private static ?string $transaction = null;
public const CONTEXT_TRANSACTION = 'sentry.integration.transaction';

public function setupOnce(): void
{
Expand Down Expand Up @@ -90,12 +91,12 @@ public static function configureScope(callable $callback): void

public static function getTransaction(): ?string
{
return self::$transaction;
return Context::get(self::CONTEXT_TRANSACTION);
}

public static function setTransaction(?string $transaction): void
{
self::$transaction = $transaction;
Context::set(self::CONTEXT_TRANSACTION, $transaction);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/src/Metrics/Listener/OnBeforeHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function process(object $event): void
return;
}

Constants::$runningInCommand = true;
Constants::setRunningInCommand();

if ($this->feature->isCommandMetricsEnabled() && $this->container->has(EventDispatcherInterface::class)) {
$this->container->get(EventDispatcherInterface::class)->dispatch(new MetricFactoryReady());
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/src/Metrics/Listener/OnMetricFactoryReady.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function process(object $event): void

$serverStatsFactory = null;

if (! SentryConstants::$runningInCommand) {
if (! SentryConstants::runningInCommand()) {
if ($this->container->has(SwooleServer::class) && $server = $this->container->get(SwooleServer::class)) {
if ($server instanceof SwooleServer) {
$serverStatsFactory = fn (): array => $server->stats();
Expand Down
112 changes: 112 additions & 0 deletions tests/Sentry/CoroutineScopedStateTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

declare(strict_types=1);
/**
* This file is part of friendsofhyperf/components.
*
* @link https://github.com/friendsofhyperf/components
* @document https://github.com/friendsofhyperf/components/blob/main/README.md
* @contact huangdijia@gmail.com
*/

namespace FriendsOfHyperf\Tests\Sentry;

use FriendsOfHyperf\Sentry\Constants;
use FriendsOfHyperf\Sentry\Integration;
use FriendsOfHyperf\Tests\TestCase;
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;

/**
* Verifies that process-level static state previously shared between requests
* is now stored in the coroutine scoped Hyperf\Context\Context.
*
* Every test method runs inside `Swoole\Coroutine::run()` (see
* FriendsOfHyperf\CoPHPUnit\Concerns\RunTestsInCoroutine), so Hyperf
* Context values are isolated per coroutine.
*
* @internal
*/
class CoroutineScopedStateTest extends TestCase
{
public function testTransactionCanBeSetAndClearedWithinCoroutine(): void
{
$this->assertGreaterThan(0, Coroutine::getCid());

Integration::setTransaction('A');
$this->assertSame('A', Integration::getTransaction());

Integration::setTransaction(null);
$this->assertNull(Integration::getTransaction());
}

public function testTransactionIsIsolatedBetweenCoroutines(): void
{
$values = [];
$firstSet = new Channel(1);
$secondChecked = new Channel(1);
$finished = new Channel(2);

Coroutine::create(function () use ($firstSet, $secondChecked, $finished, &$values): void {
Integration::setTransaction('A');
$firstSet->push(true); // Signal that 'A' has been set.
$secondChecked->pop(); // Wait until the second coroutine verified its own value.
$values['first'] = Integration::getTransaction();
$finished->push(true);
});

Coroutine::create(function () use ($firstSet, $secondChecked, $finished, &$values): void {
$firstSet->pop(); // Wait until the first coroutine set 'A'.
Integration::setTransaction('B');
$values['second'] = Integration::getTransaction();
$secondChecked->push(true); // Resume the first coroutine.
$finished->push(true);
});

$finished->pop();
$finished->pop();

$this->assertSame('A', $values['first']);
$this->assertSame('B', $values['second']);
}

public function testRunningInCommandDefaultsToFalseAndCanBeEnabledWithinCoroutine(): void
{
$this->assertFalse(Constants::runningInCommand());

Constants::setRunningInCommand();
$this->assertTrue(Constants::runningInCommand());

Constants::setRunningInCommand(false);
$this->assertFalse(Constants::runningInCommand());
}

public function testRunningInCommandIsIsolatedBetweenCoroutines(): void
{
$values = [];
$firstSet = new Channel(1);
$secondChecked = new Channel(1);
$finished = new Channel(2);

Coroutine::create(function () use ($firstSet, $secondChecked, $finished, &$values): void {
Constants::setRunningInCommand();
$firstSet->push(true); // Signal that the flag has been set.
$secondChecked->pop(); // Wait until the second coroutine verified its own value.
$values['first'] = Constants::runningInCommand();
$finished->push(true);
});

Coroutine::create(function () use ($firstSet, $secondChecked, $finished, &$values): void {
$firstSet->pop(); // Wait until the first coroutine set the flag.
$values['second'] = Constants::runningInCommand(); // Unset in this coroutine.
$secondChecked->push(true); // Resume the first coroutine.
$finished->push(true);
});

$finished->pop();
$finished->pop();

$this->assertTrue($values['first']);
$this->assertFalse($values['second']);
}
}
Loading