Fork me on GitHub
#graphql
<
2021-07-28
>
thumbnail06:07:00

We use lacinia with the component library and reloaded workflow. We have a component for pedestal and one for the schema-provider. It has been great so far but the component has a lot of dependencies (> 150) which makes reloading slow when one of the resolvers is changed. Any tips on how to improve this?

thumbnail06:07:34

I am aware that lacinia can resolve vars when they’re passed (to skip compiling the schema on each reset), but in my testing this shaved 2 seconds off, the biggest factor seems to be reloading the files

vemv14:07:20

if the bottleneck is code reloading itself (as opposed to starting/stopping) you might benefit from parallelisation I've used a WIP parallel impl of refresh for the whole year, it works nice enough that I can forget about it But I should fix/test a couple things before sharing it

vemv14:07:54

Other than that, another line of research is inspecting the "dependency paths" (from ns1 to ns2) and verifying if they make sense If it doesn't make sense to refresh/reset ns2 ns/component whenever ns1 changes, you might have an architectural problem at the same time, the whole Reloaded workflow is very pessimistic when it comes to consider x a dependency of y. So it's not rare at all to end up "reloading the world" whenever something changes

thumbnail15:07:21

Yeah it's strictly reloading; the reset takes 1-2 seconds longer than a similar refresh. The dependencies all "make sense", or at least, i think they do 😬. Currently all types that need resolvers have their own namespaces. They are not inter-connected at all. Except that the graphql-component which provides the schema needs a reference to all those resolvers. Which means that if a resolver changes, the change propegates to graphql-component -> all other resolvers. I made a POC which requires all resolvers lazily in the graphql-component, which 'fixes' the reload-behaviour; but requires a lookup/reload of the resolver before resolving the schema in development (which has it's own downsides)

👀 2
vemv16:07:59

I might be a little dense today but the problem might be less obvious than we think

vemv16:07:39

under this scenario if resolver1 changes, the component ns should be resolved, and the user ns too

vemv16:07:20

but that should be it, right? i.e. resolver2 has no reason to be reloaded unless there's something like a reference in the other direction, such that resolver2 depends (directly or not) on the graphql component

vemv16:07:03

it can be good to try determining this sort of unnecessary dependencies. Sometimes they happen due to bad modularization

vemv17:07:15

I checked just now, and when I change resolver1 at my work codebase, none of the other resolvers get refreshed. That's good :thumbsup: -test namespaces from disparate resolvers get refreshed though. That's because they all depend on a central system ns which depends on graphql and then on resolver1 . That also makes sense, it's a good use case for parallelization (because normally nothing depends on -test namespaces)

thumbnail17:07:47

Hmmm, i thought that resolver2 would be refreshed because it's a dependency of the graphql.component; but maybe there's another reason why it's reloaded. I'll have to do some more experimenting. Thanks for thinking along!

👍 2
thumbnail18:07:07

❤️ Thanks for that direction. It was indeed our text-fixture's depenency on the system, that made it circular and load every resolver as a result. Now I'll take a look if I can fix that hammock

🍻 2
thumbnail19:07:59

Success , we had a fixture which'd built the system, which is now defered until runtime of the test instead of compile time, which speeds up loading the test, and also fixes the need of the compile-time dependency. Thanks again!

vemv19:07:30

🙌 very happy to hear!

vemv19:07:12

it would be interesting to lint for cycles 👀 circular dependencies are already forbidden by t.n itself, but as we saw here, there are non-critical cycles that can get big without one noticing

2
thumbnail19:07:45

It has been a bother for a while, obviously growing over time. Pinpointing was a bit of a headscratcher tho. Maybe a linter could've helped here

thumbnail19:07:59

But the namespace with 150 requires should've been a sign

😅 2
eraserhd18:07:27

Does Lacinia have a way of generating GraphQL schema? My org wants it for registering with backstage.

eraserhd18:07:53

Specifically, I mean GraphQL SDL.

gklijs18:07:40

No, but since it's just Clojure/EDN I think it's easy to create one based on something. Guess introspec PostgreSQL and create the schema and resolvers based on that should be doable. I know with Kotlin it can be done with annotations on functions, but in that case they are fully typed. Maybe something with spec could also work.

eraserhd19:07:33

Yeah, it's easy, and it definitely looks like it isn't present yet. I'll make a PR.

eraserhd19:07:34

Proposal: com.walmartlabs.lacinia.schema/graphql-sdl or com.walmartlabs.lacinia.schema/graphql-sdl-text.

orestis06:07:22

We have used a 3rd-party tool, pointing it to our /graphql http endpoint, and it generated the schema for us. I don't recall the name, we ended up not needing it.

eins7808:07:16

We have also worked around this with a tool that gets the schema from the API endpoint (via the IntrospectionQuery, the same way that GraphiQL loads the schema). Its “good enough” for us and not too much overhead because the development server is always running a we have simple shell script to dump the schema. I can provide details/pointers if anybody want to implement the same workaround.

eraserhd13:07:25

I was pointed to an npm package that does this, and am considering it. It's possible to boot the system in CI and fail the build if the user did not commit any required updates to the schema file, and wrap this node package. But this makes development more awkward.

eraserhd13:07:07

Our schema is largely auto-generated from a data dictionary in the first place.

hlship21:07:07

I started to work on this at one point, but determined that for Walmart’s needs, starting with an SDL document (rather than generating one from EDN) worked better in the overall picture. It would still be a valuable addition to be able to easily convert back from EDN to SDL.

hlship21:07:34

It would probably be OK to forbid the use of :queries, :mutations, or :subscriptions keys in the EDN schema.

eraserhd13:07:50

why forbid those?

hlship16:08:52

They’re kind of ambiguous, you end up having to fold them into the Query object (etc.); of course, if you work off the compiled schema, that’s already handled.