nuwave / lighthouse

A framework for serving GraphQL from Laravel
https://lighthouse-php.com
MIT License
3.36k stars 438 forks source link

Batchloading relations with different database connections #1252

Closed richardabear closed 2 years ago

richardabear commented 4 years ago

The Scenario

I implemented a database connection where all A Records Exists and called it connection2 B and C exist on the default database connection using a test controller I can verify that the connection and data exist and is queryable. Using a custom resolver also works.


The implementation (That doesnt work)

I created a graphQL file that basically defines the ff

extend type A {
 bRelationship: [B!]! @belongsToMany(type: "paginator", relation: "bRelationship")
}

When trying to query this I get this error

SQLSTATE[42P01]: Undefined table: 7 ERROR:  relation \"domain_names\" does not exist\nLINE 1: select \"organizations\".*, (select count(*) from \"domain_name...\n                                                        ^ (SQL: select \"organizations\".*, (select count(*) from \"domain_names\" inner join \"domain_name_organization\" on \"domain_names\".\"id\" = \"domain_name_organization\".\"domain_name_id\" where \"organizations\".\"id\" = \"domain_name_organization\".\"organization_id\" and \"domain_names\".\"deleted_at\" is null) as \"domain_names_count\" from \"organizations\" where \"organizations\".\"id\" in (1))

The implementation (That works)

extend type A {
B: [B!]! @paginate(resolver: "App\\GraphQL\\Queries\\A@resolveB")
}

And the contents of that resolver look something like this:

 $values = $rootValue->domainNames()->paginate();
 return $values;

This resolves the query and returns a propper graphql response.


Expected behavior/Solution I think there is a problem when trying to resolve the "paginator" type on the @belongsToMany relationship (or quite possibly all relationships, I havent tested) that is preventing it from being resolved.

I think that writing custom resolvers for each of these implementations defeats the purpose of having the custom default directives in the first place.

Environment

Lighthouse Version: 4.10 Laravel Version: 6

spawnia commented 4 years ago

Thanks for the detailed description. The next step towards resolving this issue would be the have a PR with a failing test case. That might be a bit tricky, since the current setup only has a single database connection.

spawnia commented 3 years ago

I have recently made substantial changes to paginated relation batch loading, can you check if the issue persists with Lighthouse v5.1.0?

richardabear commented 3 years ago

Hi @spawnia unfortunately i no longer have access to the repository that was using this. When i get some time possibly on the weekend or next weekend ill try it out. But im not sure i can recall the exact architecture i used for this.

dolfje commented 3 years ago

I have the same problem with the latest release of Lighthouse. When I try to execute a Graphql with hasMany that spans multiple connections, it will fail.

It tries to execute the following query:

select `id`, (select count(*) from accounts where user.id = account.user_id) as `accounts_count` from `users` where `users`.`id` in (TEST INGENICO))

While the Eloquent Account model is another connection than the User model. So this cannot be combined.

schema.graphql

type Query {
    me: User @auth
}

type User {
    accounts: [Account!]!  @hasMany(type: PAGINATOR, defaultCount: 25)
}

query

query {
  me {
    accounts {
      data {id}
    }
  }
}

Stacktrace:

[
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Connection.php",
          "line": 652,
          "call": "Illuminate\\Database\\Connection::runQueryCallback()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Connection.php",
          "line": 360,
          "call": "Illuminate\\Database\\Connection::run()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php",
          "line": 2350,
          "call": "Illuminate\\Database\\Connection::select()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php",
          "line": 2338,
          "call": "Illuminate\\Database\\Query\\Builder::runSelect()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php",
          "line": 2872,
          "call": "Illuminate\\Database\\Query\\Builder::Illuminate\\Database\\Query\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php",
          "line": 2339,
          "call": "Illuminate\\Database\\Query\\Builder::onceWithColumns()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php",
          "line": 615,
          "call": "Illuminate\\Database\\Query\\Builder::get()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php",
          "line": 599,
          "call": "Illuminate\\Database\\Eloquent\\Builder::getModels()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Execution/ModelsLoader/CountModelsLoader.php",
          "line": 76,
          "call": "Illuminate\\Database\\Eloquent\\Builder::get()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Execution/ModelsLoader/PaginatedModelsLoader.php",
          "line": 43,
          "call": "Nuwave\\Lighthouse\\Execution\\ModelsLoader\\CountModelsLoader::loadCount()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Execution/BatchLoader/RelationBatchLoader.php",
          "line": 80,
          "call": "Nuwave\\Lighthouse\\Execution\\ModelsLoader\\PaginatedModelsLoader::load()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Execution/BatchLoader/RelationBatchLoader.php",
          "line": 52,
          "call": "Nuwave\\Lighthouse\\Execution\\BatchLoader\\RelationBatchLoader::resolve()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromise.php",
          "line": 67,
          "call": "Nuwave\\Lighthouse\\Execution\\BatchLoader\\RelationBatchLoader::Nuwave\\Lighthouse\\Execution\\BatchLoader\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromise.php",
          "line": 53,
          "call": "GraphQL\\Executor\\Promise\\Adapter\\SyncPromise::GraphQL\\Executor\\Promise\\Adapter\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromiseAdapter.php",
          "line": 149,
          "call": "GraphQL\\Executor\\Promise\\Adapter\\SyncPromise::runQueue()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/webonyx/graphql-php/src/GraphQL.php",
          "line": 105,
          "call": "GraphQL\\Executor\\Promise\\Adapter\\SyncPromiseAdapter::wait()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 213,
          "call": "GraphQL\\GraphQL::executeQuery()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 164,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeQuery()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 118,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeOperation()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Support/Utils.php",
          "line": 98,
          "call": "Nuwave\\Lighthouse\\GraphQL::Nuwave\\Lighthouse\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/GraphQL.php",
          "line": 117,
          "call": "Nuwave\\Lighthouse\\Support\\Utils::applyEach()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Support/Http/Controllers/GraphQLController.php",
          "line": 32,
          "call": "Nuwave\\Lighthouse\\GraphQL::executeOperationOrOperations()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
          "line": 48,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Controllers\\GraphQLController::__invoke()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
          "line": 261,
          "call": "Illuminate\\Routing\\ControllerDispatcher::dispatch()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
          "line": 204,
          "call": "Illuminate\\Routing\\Route::runController()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 695,
          "call": "Illuminate\\Routing\\Route::run()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 128,
          "call": "Illuminate\\Routing\\Router::Illuminate\\Routing\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Support/Http/Middleware/AttemptAuthentication.php",
          "line": 33,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AttemptAuthentication::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/nuwave/lighthouse/src/Support/Http/Middleware/AcceptJson.php",
          "line": 27,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Nuwave\\Lighthouse\\Support\\Http\\Middleware\\AcceptJson::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 103,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 697,
          "call": "Illuminate\\Pipeline\\Pipeline::then()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 672,
          "call": "Illuminate\\Routing\\Router::runRouteWithinStack()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 636,
          "call": "Illuminate\\Routing\\Router::runRoute()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
          "line": 625,
          "call": "Illuminate\\Routing\\Router::dispatchToRoute()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 166,
          "call": "Illuminate\\Routing\\Router::dispatch()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 128,
          "call": "Illuminate\\Foundation\\Http\\Kernel::Illuminate\\Foundation\\Http\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
          "line": 21,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
          "line": 31,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
          "line": 21,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
          "line": 40,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
          "line": 27,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
          "line": 86,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/fruitcake/laravel-cors/src/HandleCors.php",
          "line": 52,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Fruitcake\\Cors\\HandleCors::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php",
          "line": 39,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 167,
          "call": "Illuminate\\Http\\Middleware\\TrustProxies::handle()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
          "line": 103,
          "call": "Illuminate\\Pipeline\\Pipeline::Illuminate\\Pipeline\\{closure}()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 141,
          "call": "Illuminate\\Pipeline\\Pipeline::then()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
          "line": 110,
          "call": "Illuminate\\Foundation\\Http\\Kernel::sendRequestThroughRouter()"
        },
        {
          "file": "/srv/apps/n000nikos/res.uwsoftware.be/public/index.php",
          "line": 52,
          "call": "Illuminate\\Foundation\\Http\\Kernel::handle()"
        }
      ]
dolfje commented 3 years ago

Solved it by disabling config('lighthouse.batchload_relations'). Maybe it is would be handy to extract that value in the directive @hasMany(batchload:false)

spawnia commented 3 years ago

Is it possible for Lighthouse to detect which connection a particular relation/model uses? In that case, it could automatically fall back to not using batch loading.

dolfje commented 3 years ago

Yes, that is possible, see https://github.com/nuwave/lighthouse/pull/1945/files

spawnia commented 2 years ago

Fixed in https://github.com/nuwave/lighthouse/pull/1979