rmosolgo / graphql-ruby

Ruby implementation of GraphQL
http://graphql-ruby.org
MIT License
5.38k stars 1.39k forks source link

OperationStore index appears to fail with nil comparsion #4940

Closed tmtrademarked closed 5 months ago

tmtrademarked commented 5 months ago

Describe the bug

When loading the admin page for the OperationStore index, we get an error:

NoMethodError undefined method `>' for nil:NilClass

Versions

graphql version: graphql 2.3.1, graphql-pro 1.27.1 rails (or other framework): 6.1.7.7

Steps to reproduce

1) Open the admin page for your GraphQL server 2) Select Operation Store -> Index

Expected behavior

A clear and concise description of what you expected to happen.

Actual behavior

From drilling into the operation store, this appears to happen at this line:

/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard/templates/operation_store_component/operations_index.erb:23

That appears to building up the entry summary:

<% entries.each do |entry| %>
      <tr>
        <td><%= link_to(entry.name, operations_index_entry_path(name: entry.name)) %></td>
        <td><%= entry.references_count %><% if entry.archived_references_count > 0 %> <span class="muted">(<%= entry.archived_references_count %> archived)</span><% end %></td>
        <td><%= localize_date(entry.last_used_at) %></td>
      </tr>
    <% end %>

This makes me wonder if perhaps there's some schema migration or other operation that needs to be applied to our DB that got missed somehow?

Place full backtrace here (if a Ruby exception is involved):

Click to view exception backtrace ``` NoMethodError: undefined method `>' for nil:NilClass block (2 levels) in to_binding(/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard/templates/operation_store_component/operations_index.erb:23) each(/usr/local/bundle/gems/activerecord-6.1.7.7/lib/active_record/relation/delegation.rb:88) each(/usr/local/bundle/gems/activerecord-6.1.7.7/lib/active_record/relation/delegation.rb:88) block in to_binding(/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard/templates/operation_store_component/operations_index.erb:20) eval(/usr/local/lib/ruby/2.7.0/erb.rb:905) result(/usr/local/lib/ruby/2.7.0/erb.rb:905) render(/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard/template.rb:24) render_view(/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard/dispatcher.rb:103) dispatch(/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard/dispatcher.rb:29) call(/usr/local/bundle/gems/graphql-pro-1.27.1/lib/graphql/pro/dashboard.rb:56) block in (/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/mapper.rb:20) serve(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/mapper.rb:49) block in serve(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:50) each(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:32) serve(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/journey/router.rb:32) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/routing/route_set.rb:842) memoized_call(/usr/local/bundle/gems/flipper-0.24.1/lib/flipper/middleware/memoizer.rb:77) call(/usr/local/bundle/gems/flipper-0.24.1/lib/flipper/middleware/memoizer.rb:42) call!(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:202) call(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:169) call!(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:202) call(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:169) call!(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:202) call(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:169) call!(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:202) call(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:169) call!(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:202) call(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/strategy.rb:169) call(/usr/local/bundle/gems/omniauth-2.1.1/lib/omniauth/builder.rb:44) call(/opt/blueapron.com/lib/middleware/version_endpoint.rb:16) call(/opt/blueapron.com/lib/middleware/force_admin.rb:17) call(/opt/blueapron.com/lib/middleware/force_www.rb:15) call(/usr/local/bundle/gems/rack-attack-6.0.0/lib/rack/attack.rb:158) call!(/usr/local/bundle/gems/flipper-0.24.1/lib/flipper/middleware/setup_env.rb:45) call(/usr/local/bundle/gems/flipper-0.24.1/lib/flipper/middleware/setup_env.rb:40) block in call(/usr/local/bundle/gems/warden-1.2.9/lib/warden/manager.rb:36) catch(/usr/local/bundle/gems/warden-1.2.9/lib/warden/manager.rb:34) call(/usr/local/bundle/gems/warden-1.2.9/lib/warden/manager.rb:34) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/tempfile_reaper.rb:15) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/etag.rb:27) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/conditional_get.rb:27) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/head.rb:12) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/http/permissions_policy.rb:22) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/http/content_security_policy.rb:19) context(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/session/abstract/id.rb:266) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/session/abstract/id.rb:260) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/cookies.rb:697) block in call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/callbacks.rb:27) run_callbacks(/usr/local/bundle/gems/activesupport-6.1.7.7/lib/active_support/callbacks.rb:98) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/callbacks.rb:26) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/actionable_exceptions.rb:18) block in call(/usr/local/bundle/gems/rollbar-3.3.2/lib/rollbar/middleware/rails/rollbar.rb:25) scoped(/usr/local/bundle/gems/rollbar-3.3.2/lib/rollbar.rb:145) call(/usr/local/bundle/gems/rollbar-3.3.2/lib/rollbar/middleware/rails/rollbar.rb:22) call(/usr/local/bundle/gems/ddtrace-1.22.0/lib/datadog/tracing/contrib/rails/middlewares.rb:19) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/debug_exceptions.rb:29) call_with_rollbar(/usr/local/bundle/gems/rollbar-3.3.2/lib/rollbar/middleware/rails/show_exceptions.rb:22) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/show_exceptions.rb:33) call(/usr/local/bundle/gems/blueapron-jwt-4.1.1/lib/blueapron/jwt/middleware/rack.rb:30) call_app(/usr/local/bundle/gems/lograge-0.12.0/lib/lograge/rails_ext/rack/logger.rb:18) block in call(/usr/local/bundle/gems/railties-6.1.7.7/lib/rails/rack/logger.rb:26) block in tagged(/usr/local/bundle/gems/activesupport-6.1.7.7/lib/active_support/tagged_logging.rb:99) tagged(/usr/local/bundle/gems/activesupport-6.1.7.7/lib/active_support/tagged_logging.rb:37) tagged(/usr/local/bundle/gems/activesupport-6.1.7.7/lib/active_support/tagged_logging.rb:99) call(/usr/local/bundle/gems/railties-6.1.7.7/lib/rails/rack/logger.rb:26) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/remote_ip.rb:81) call(/usr/local/bundle/gems/request_store-1.5.1/lib/request_store/middleware.rb:19) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/request_id.rb:26) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/method_override.rb:24) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/runtime.rb:22) call(/usr/local/bundle/gems/activesupport-6.1.7.7/lib/active_support/cache/strategy/local_cache_middleware.rb:29) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/executor.rb:14) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/static.rb:24) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/sendfile.rb:110) call(/usr/local/bundle/gems/actionpack-6.1.7.7/lib/action_dispatch/middleware/host_authorization.rb:142) call(/usr/local/bundle/gems/rack-utf8_sanitizer-1.7.0/lib/rack/utf8_sanitizer.rb:22) call(/usr/local/bundle/gems/broth-4.16.1/lib/broth/instrumentation/web.rb:20) call(/usr/local/bundle/gems/ddtrace-1.22.0/lib/datadog/tracing/contrib/rack/middlewares.rb:109) call(/usr/local/bundle/gems/railties-6.1.7.7/lib/rails/engine.rb:539) forward(/usr/local/bundle/gems/rack-cache-1.7.0/lib/rack/cache/context.rb:140) fetch(/usr/local/bundle/gems/rack-cache-1.7.0/lib/rack/cache/context.rb:249) lookup(/usr/local/bundle/gems/rack-cache-1.7.0/lib/rack/cache/context.rb:189) call!(/usr/local/bundle/gems/rack-cache-1.7.0/lib/rack/cache/context.rb:66) call(/usr/local/bundle/gems/rack-cache-1.7.0/lib/rack/cache/context.rb:51) call(/opt/blueapron.com/lib/middleware/handle_invalid_encoding.rb:10) call(/usr/local/bundle/gems/rack-2.2.8.1/lib/rack/deflater.rb:44) process_client(/usr/local/bundle/gems/unicorn-5.7.0/lib/unicorn/http_server.rb:632) process_client(/usr/local/bundle/gems/unicorn-worker-killer-0.4.4/lib/unicorn/worker_killer.rb:52) process_client(/usr/local/bundle/gems/unicorn-worker-killer-0.4.4/lib/unicorn/worker_killer.rb:92) worker_loop(/usr/local/bundle/gems/unicorn-5.7.0/lib/unicorn/http_server.rb:728) spawn_missing_workers(/usr/local/bundle/gems/unicorn-5.7.0/lib/unicorn/http_server.rb:548) maintain_worker_count(/usr/local/bundle/gems/unicorn-5.7.0/lib/unicorn/http_server.rb:562) join(/usr/local/bundle/gems/unicorn-5.7.0/lib/unicorn/http_server.rb:295) (/usr/local/bundle/gems/unicorn-5.7.0/bin/unicorn:128) load(/usr/local/bundle/bin/unicorn:23) (/usr/local/bundle/bin/unicorn:23) load(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:74) kernel_load(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:74) run(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/cli/exec.rb:28) exec(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/cli.rb:463) run(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/command.rb:27) invoke_command(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:126) dispatch(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor.rb:387) dispatch(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/cli.rb:27) start(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/vendor/thor/lib/thor/base.rb:466) start(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/cli.rb:18) block in (/usr/local/bundle/gems/bundler-1.17.3/exe/bundle:30) with_friendly_errors(/usr/local/bundle/gems/bundler-1.17.3/lib/bundler/friendly_errors.rb:124) (/usr/local/bundle/gems/bundler-1.17.3/exe/bundle:22) load(/usr/local/bin/bundle:23)
(/usr/local/bin/bundle:23) ```
rmosolgo commented 5 months ago

Hey, sorry for the trouble and thanks for reporting this! "Archiving" was a later addition to the OperationStore, but I certainly intended to retain backwards compatibility for systems that didn't migrate or hadn't migrated yet. I'll investigate this soon, and if you want to try migrating your database in the meantime, you can find the migration notes here: https://graphql-ruby.org/operation_store/active_record_backend.html#database-update

tmtrademarked commented 5 months ago

Hmm - we did the migration referenced here a while ago. I can confirm that both of those columns are in the DB. But the error appears to be complaining about archived_references_count, which from what I can tell is actually just computed in a join.

So it sounds like there's nothing that jumps out obviously as a step we missed, which makes me wonder if maybe we have something else slightly unexpected in our system.

rmosolgo commented 5 months ago

Yeah... very weird. Maybe that SUM(...) is somehow returning null instead of 0. What database are you using?

tmtrademarked commented 5 months ago

We're using Postgres 12.18! Sorry, should have included that earlier.

rmosolgo commented 5 months ago

No worries, thanks. I was able to replicate this issue when GraphQLIndexReference records are missing from the database. I'm not sure how that could happen, but it's easy enough to recreate them in a Rails console:

MySchema.operation_store.reindex

I expect if you run that in a console, this view will be fixed for you.

In any case, I also released graphql-pro 1.27.2 with more graceful handling for this case, printing a message suggesting that same fix.

Could you try that fix, or try the new version, and let me know how it goes for you?

tmtrademarked commented 5 months ago

Hmm - MySchema.operation_store.reindex seemed to fix the backend, but that command seems to run forever. Am I missing something, or does the reindex method never actually increment current_page? It seems odd that this is an eternal loop for me, given that at least iterations should increment to 1000 and stop?

tmtrademarked commented 5 months ago

Hmm, I spoke prematurely - it no longer has the same error, but the index is pretty obviously incomplete. :-/

rmosolgo commented 5 months ago

Derp... so sorry about that. I just released graphql-pro v1.27.3 with a fixed (and properly tested...) multi-page .reindex behavior. Could you try again on the new version?

tmtrademarked commented 5 months ago

Ah ha! That got us back in shape. Thanks a ton, @rmosolgo !

rmosolgo commented 5 months ago

Glad to hear it. Sorry for the trouble and thanks for working through it with me!