rmosolgo/graphql-ruby

OperationStore index appears to fail with nil comparsion

tmtrademarked opened this issue · 10 comments

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 <class:Constraints>(/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)

<top (required)>(/usr/local/bundle/gems/unicorn-5.7.0/bin/unicorn:128)

load(/usr/local/bundle/bin/unicorn:23)

<top (required)>(/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 <top (required)>(/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)

<top (required)>(/usr/local/bundle/gems/bundler-1.17.3/exe/bundle:22)

load(/usr/local/bin/bundle:23)

<main>(/usr/local/bin/bundle:23)

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

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.

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

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

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?

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?

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

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?

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

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