Skip to content

Commit

Permalink
Support federated tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
cappuc authored Dec 7, 2023
1 parent 6b0fdde commit 3806fb6
Show file tree
Hide file tree
Showing 61 changed files with 7,574 additions and 102 deletions.
5 changes: 4 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,8 @@ trim_trailing_whitespace = false
[*.neon]
indent_size = 2

[*.yml]
[{*.yml, *.yaml}]
indent_size = 2

[*.proto]
indent_size = 2
40 changes: 40 additions & 0 deletions .github/workflows/proto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: "Build protobuf"
on:
push:
branches:
- '*'
paths:
- '**.proto'
- 'buf.gen.yaml'

jobs:
proto:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}

- uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: mbstring
php-version: 8.2

- run: composer install --no-interaction --no-progress --no-suggest

- uses: bufbuild/buf-setup-action@v1

- run: |
buf generate
rm -rf src/Tracing/FederatedTracing/Proto
mv proto-tmp/Nuwave/Lighthouse/Tracing/FederatedTracing/Proto src/Tracing/FederatedTracing/Proto
rm -rf proto-tmp
- run: vendor/bin/php-cs-fixer fix src/Tracing/FederatedTracing/Proto

- run: git pull

- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Apply proto changes
28 changes: 28 additions & 0 deletions .github/workflows/update-reports-proto.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "Update reports.proto"
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * 0"

jobs:
proto:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}

- run: curl -sSfo src/Tracing/FederatedTracing/reports.proto https://usage-reporting.api.apollographql.com/proto/reports.proto

- run: |
sed -i 's/ \[(js_use_toArray) = true]//g' src/Tracing/FederatedTracing/reports.proto
sed -i 's/ \[(js_preEncoded) = true]//g' src/Tracing/FederatedTracing/reports.proto
sed -i '3 i option php_namespace = "Nuwave\\\\Lighthouse\\\\Tracing\\\\FederatedTracing\\\\Proto";' src/Tracing/FederatedTracing/reports.proto
sed -i '4 i option php_metadata_namespace = "Nuwave\\\\Lighthouse\\\\Tracing\\\\FederatedTracing\\\\Proto\\\\Metadata";' src/Tracing/FederatedTracing/reports.proto
- uses: peter-evans/create-pull-request@v5
with:
title: 'Updated `reports.proto`'
commit_message: Updated `reports.proto`
branch: patch/update-reports-proto
delete-branch: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ phpunit.xml
.php-cs-fixer.cache
build
phpstan-tmp-dir
proto-tmp

# composer
composer.phar
Expand Down
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ $user = User::create([
]);
```

## Working with proto files

Lighthouse uses [protobuf](https://developers.google.com/protocol-buffers) files for [federated tracing](src/Tracing/FederatedTracing/reports.proto).
When updating the proto files, the PHP classes need to be regenerated.
The generation is done with [buf](https://buf.build/docs/generate/overview).
The `make proto` command generates the new PHP classes and replace the old ones.

## Documentation

### External
Expand Down
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,19 @@ docs: up ## Render the docs in a development server

docs/node_modules: up docs/package.json docs/yarn.lock ## Install yarn dependencies
${dcnode} yarn

.PHONY: proto/update-reports
proto/update-reports:
${dcphp} curl -sSfo src/Tracing/FederatedTracing/reports.proto https://usage-reporting.api.apollographql.com/proto/reports.proto
${dcphp} sed -i 's/ \[(js_use_toArray) = true]//g' src/Tracing/FederatedTracing/reports.proto
${dcphp} sed -i 's/ \[(js_preEncoded) = true]//g' src/Tracing/FederatedTracing/reports.proto
${dcphp} sed -i '3 i option php_namespace = "Nuwave\\\\Lighthouse\\\\Tracing\\\\FederatedTracing\\\\Proto";' src/Tracing/FederatedTracing/reports.proto
${dcphp} sed -i '4 i option php_metadata_namespace = "Nuwave\\\\Lighthouse\\\\Tracing\\\\FederatedTracing\\\\Proto\\\\Metadata";' src/Tracing/FederatedTracing/reports.proto

.PHONY: proto
proto:
docker run --rm --volume ".:/tmp" --workdir /tmp bufbuild/buf generate
${dcphp} rm -rf src/Tracing/FederatedTracing/Proto
${dcphp} mv proto-tmp/Nuwave/Lighthouse/Tracing/FederatedTracing/Proto src/Tracing/FederatedTracing/Proto
${dcphp} rm -rf proto-tmp
$(MAKE) php-cs-fixer
7 changes: 7 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: v1
managed:
enabled: true
plugins:
# For new releases, see https://github.com/protocolbuffers/protobuf/releases and https://buf.build/protocolbuffers/php
- plugin: buf.build/protocolbuffers/php:v25.1
out: proto-tmp
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"dms/phpunit-arraysubset-asserts": "^0.4 || ^0.5",
"ergebnis/composer-normalize": "^2.2.2",
"fakerphp/faker": "^1.21",
"google/protobuf": "^3.21",
"laravel/framework": "^9 || ^10",
"laravel/legacy-factories": "^1.1.1",
"laravel/lumen-framework": "^9 || ^10 || dev-master",
Expand Down Expand Up @@ -77,7 +78,9 @@
"laravel/scout": "Required for the @search directive",
"mll-lab/graphql-php-scalars": "Useful scalar types, required for @whereConditions",
"mll-lab/laravel-graphiql": "A graphical interactive in-browser GraphQL IDE - integrated with Laravel",
"pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver"
"pusher/pusher-php-server": "Required when using the Pusher Subscriptions driver",
"google/protobuf": "Required when using the tracing driver federated-tracing",
"ext-protobuf": "Improve protobuf serialization performance (used for tracing)"
},
"minimum-stability": "dev",
"prefer-stable": true,
Expand Down
16 changes: 13 additions & 3 deletions docs/master/federation/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ extend schema @link(
)
```

## Federated tracing

In order to use federated tracing, you need to enabled [tracing](../performance/tracing.md)
and set the driver to `Nuwave\Lighthouse\Tracing\FederatedTracing\FederatedTracing::class` in your `config/lighthouse.php`:

```php
'tracing' => [
'driver' => Nuwave\Lighthouse\Tracing\FederatedTracing\FederatedTracing::class,
],
```

Note that federated tracing requires `google/protobuf` to be installed (for better performance you can also install the `protobuf` php extension).

### Unsupported features

Some features of the Apollo Federation specification **are not supported** by Lighthouse:
Expand Down Expand Up @@ -86,6 +99,3 @@ type Book @federation__shareable {
}
```

#### Federated tracing

[Federated tracing](https://www.apollographql.com/docs/federation/metrics) is not supported.
15 changes: 14 additions & 1 deletion docs/master/performance/tracing.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Tracing

Tracing offers field-level performance monitoring for your GraphQL server.
Lighthouse follows the [Apollo Tracing response format](https://github.com/apollographql/apollo-tracing#response-format).

## Setup

Expand All @@ -12,3 +11,17 @@ Add the service provider to your `config/app.php`
\Nuwave\Lighthouse\Tracing\TracingServiceProvider::class,
],
```

## Drivers

Lighthouse tracing is implemented though drivers, this allows supporting different tracing formats.

Lighthouse includes the following drivers:

- `Nuwave\Lighthouse\Tracing\ApolloTracing\ApolloTracing::class` (default) which implements [Apollo Tracing response format](https://github.com/apollographql/apollo-tracing#response-format)
- `Nuwave\Lighthouse\Tracing\FederatedTracing\FederatedTracing::class` which implements [Apollo Federated tracing](https://www.apollographql.com/docs/federation/metrics/)

### Federated tracing

Federated tracing driver requires `google/protobuf` to be installed.
For better performance you can also install the `protobuf` php extension.
1 change: 1 addition & 0 deletions php.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ RUN apt-get update && \
mysqli \
pdo_mysql \
intl \
bcmath \
&& rm -rf /var/lib/apt/lists/* \
&& pecl install \
xdebug \
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ parameters:
# laravel/pennant requires Laravel 10
- src/Pennant
- tests/Integration/Pennant
- src/Tracing/FederatedTracing/Proto # Generated classes from protobuf
ignoreErrors:
# PHPStan does not get it
- '#Parameter \#1 \$callback of static method Closure::fromCallable\(\) expects callable\(\): mixed, array{object, .*} given\.#'
Expand Down
1 change: 1 addition & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
PHPUnitSetList::REMOVE_MOCKS,
]);
$rectorConfig->skip([
__DIR__ . '/src/Tracing/FederatedTracing/Proto', // Generated code
__DIR__ . '/tests/database/migrations', // Does not fit autoloading standards
__DIR__ . '/tests/LaravelPhpdocAlignmentFixer.php', // Copied from Laravel
CallableThisArrayToAnonymousFunctionRector::class, // Callable in array form is shorter and more efficient
Expand Down
10 changes: 9 additions & 1 deletion src/Events/BuildExtensionsResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

namespace Nuwave\Lighthouse\Events;

use GraphQL\Executor\ExecutionResult;

/**
* Fires after a query was resolved.
*
* Listeners may return a @see \Nuwave\Lighthouse\Execution\ExtensionsResponse
* to include in the response.
*/
class BuildExtensionsResponse {}
class BuildExtensionsResponse
{
public function __construct(
/** The result of resolving a single operation. */
public ExecutionResult $result,
) {}
}
2 changes: 1 addition & 1 deletion src/GraphQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public function executeParsedQuery(

/** @var array<\Nuwave\Lighthouse\Execution\ExtensionsResponse|null> $extensionsResponses */
$extensionsResponses = (array) $this->eventDispatcher->dispatch(
new BuildExtensionsResponse(),
new BuildExtensionsResponse($result),
);

foreach ($extensionsResponses as $extensionsResponse) {
Expand Down
76 changes: 76 additions & 0 deletions src/Tracing/ApolloTracing/ApolloTracing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php declare(strict_types=1);

namespace Nuwave\Lighthouse\Tracing\ApolloTracing;

use Illuminate\Support\Carbon;
use Nuwave\Lighthouse\Events\BuildExtensionsResponse;
use Nuwave\Lighthouse\Events\StartExecution;
use Nuwave\Lighthouse\Events\StartRequest;
use Nuwave\Lighthouse\Execution\ExtensionsResponse;
use Nuwave\Lighthouse\Execution\ResolveInfo;
use Nuwave\Lighthouse\Tracing\Tracing;
use Nuwave\Lighthouse\Tracing\TracingUtilities;

/** See https://github.com/apollographql/apollo-tracing#response-format. */
class ApolloTracing implements Tracing
{
use TracingUtilities;

/** The point in time when the request was initially started. */
protected Carbon $executionStartAbsolute;

/**
* The precise point in time when the request was initially started.
*
* This is either in seconds with microsecond precision (float) or nanoseconds (int).
*/
protected int|float $executionStartPrecise;

/**
* Trace entries for a single query execution.
*
* @var array<int, array<string, mixed>>
*/
protected array $resolverTraces = [];

public function handleStartRequest(StartRequest $startRequest): void {}

public function handleStartExecution(StartExecution $startExecution): void
{
$this->executionStartAbsolute = Carbon::now();
$this->executionStartPrecise = $this->timestamp();
$this->resolverTraces = [];
}

public function handleBuildExtensionsResponse(BuildExtensionsResponse $buildExtensionsResponse): ?ExtensionsResponse
{
$requestEndAbsolute = Carbon::now();
$requestEndPrecise = $this->timestamp();

return new ExtensionsResponse(
'tracing',
[
'version' => 1,
'startTime' => $this->formatTimestamp($this->executionStartAbsolute),
'endTime' => $this->formatTimestamp($requestEndAbsolute),
'duration' => $this->diffTimeInNanoseconds($this->executionStartPrecise, $requestEndPrecise),
'execution' => [
'resolvers' => $this->resolverTraces,
],
],
);
}

/** Record resolver execution time. */
public function record(ResolveInfo $resolveInfo, float|int $start, float|int $end): void
{
$this->resolverTraces[] = [
'path' => $resolveInfo->path,
'parentType' => $resolveInfo->parentType->name,
'fieldName' => $resolveInfo->fieldName,
'returnType' => $resolveInfo->returnType->toString(),
'startOffset' => $this->diffTimeInNanoseconds($this->executionStartPrecise, $start),
'duration' => $this->diffTimeInNanoseconds($start, $end),
];
}
}
Loading

0 comments on commit 3806fb6

Please sign in to comment.