Skip to content

Add custom subscription store interface#54

Open
TikiTDO wants to merge 12 commits into
anycable:masterfrom
TikiTDO:feature/postgres-signalling
Open

Add custom subscription store interface#54
TikiTDO wants to merge 12 commits into
anycable:masterfrom
TikiTDO:feature/postgres-signalling

Conversation

@TikiTDO

@TikiTDO TikiTDO commented May 22, 2026

Copy link
Copy Markdown

Summary

Adds a custom subscription store interface to graphql-anycable while keeping Redis as the built-in default store.

This trims the PR down to the refactoring/interface layer requested in review. The PostgreSQL implementation has been moved to a standalone gem owned under TikiTDO: https://github.com/TikiTDO/graphql-anycable_postgresql-store

What Changed

  • Added GraphQL::AnyCable.register_subscription_store(name) { ... } for external store adapters.
  • Kept GraphQL::AnyCable.subscription_store = store for direct store injection.
  • Routed subscription reads/writes/lookups/deletes through with_subscription_store.
  • Preserved the existing Redis behavior behind GraphQL::AnyCable::SubscriptionStores::Redis.
  • Kept Redis stats working after moving Redis key-prefix constants into the Redis store.
  • Removed the bundled PostgreSQL store, pg development dependency, and PostgreSQL-specific core config.
  • Added README documentation for the custom store protocol and registration flow.
  • Clarified that Redis is only required when using the built-in Redis subscription store; external stores such as the PostgreSQL store do not require Redis for graphql-anycable subscription state.
  • Updated the changelog and added focused interface specs.

Store Interface

A custom store object implements:

  • stream_for(fingerprint)
  • fingerprints_for_topic(topic)
  • subscription_ids_for_fingerprints(fingerprints)
  • subscription_exists?(subscription_id)
  • write_subscription(subscription_id, channel_id:, data:, events:, expiration_seconds:)
  • read_subscription(subscription_id)
  • delete_channel_subscriptions(channel_id)
  • delete_subscription(subscription_id)

Reviewer Focus

  • Redis should remain the default and compatible behavior for existing users.
  • The core gem no longer depends on or references PostgreSQL.
  • External stores can register by name without monkey-patching GraphQL::AnyCable.
  • Unknown configured stores now raise an actionable registration error.
  • Redis-specific stats still work with the extracted Redis store class.

Validation

Passed against a temporary isolated Redis container:

REDIS_URL=redis://localhost:56379/6 bundle exec rspec

Passed syntax/diff checks:

bundle exec ruby -Ilib -e 'require "graphql-anycable"; store = Object.new; GraphQL::AnyCable.register_subscription_store(:custom) { store }; GraphQL::AnyCable.config.subscription_store = :custom; abort "custom store failed" unless GraphQL::AnyCable.subscription_store.equal?(store); puts "custom store registration ok"'
ruby -c lib/graphql-anycable.rb
ruby -c lib/graphql/anycable/stats.rb
ruby -c spec/graphql/anycable_spec.rb
git diff --check

Related Follow-Up

Checklist

  • Preserved Redis store behavior behind a storage abstraction
  • Added a public custom store registration API
  • Removed PostgreSQL implementation from the core gem
  • Added a changelog entry
  • Updated documentation

@palkan palkan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Finally, we can make graphql-anycable Redis-independent! That's a good idea.

However, I'm not sure that we should add Postgres adapter implementation to the core gem. I'd prefer to have it as a standalone plugin (well, tbh, and not to maintain ourself).

Let's keep only the refactoring part with a clear interface for defining and using custom adapters in this PR (so we can release it independently and quicker), and then you can create a standalone graphql-anycable_postgresql-store gem (or whatever).

@TikiTDO TikiTDO changed the title Feature: Postgres signalling Add custom subscription store interface May 25, 2026
@TikiTDO

TikiTDO commented May 25, 2026

Copy link
Copy Markdown
Author

Split it out into it's own gem, should be ready in a bit. I'll take a look at the go lib at the end of the day.

@TikiTDO TikiTDO requested a review from palkan May 25, 2026 13:06

@palkan palkan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Almost there.

Left some comments.

Note: this feature deserves a major upgrade (2.0), so introducing minor incompatible changes in the interface is fine.

Comment thread lib/graphql-anycable.rb Outdated
Comment thread lib/graphql/anycable/stats.rb Outdated
@TikiTDO TikiTDO requested a review from palkan May 27, 2026 19:46

@palkan palkan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

One more thing we've missed—Cleaner: https://github.com/anycable/graphql-anycable/blob/master/lib/graphql/anycable/cleaner.rb

We should also make it adapter-agnostic.

In the end, I think we should have the following structure for the Redis store:

graphql/anycable/subscription_stores/
  redis/
    cleaner.rb
    stats.rb
  redis.rb

That is, let's extract Stats object from the store (just re-organize the code, keep the interface) and add the cleaner object to be used by the Rake tasks.

@TikiTDO

TikiTDO commented May 28, 2026

Copy link
Copy Markdown
Author

@palkan Done

@TikiTDO TikiTDO requested a review from palkan May 28, 2026 18:28
Comment thread spec/graphql/cleaner_spec.rb Outdated
Comment thread spec/graphql/cleaner_spec.rb Outdated
Comment thread spec/graphql/stats_spec.rb Outdated
@TikiTDO

TikiTDO commented May 28, 2026

Copy link
Copy Markdown
Author

Ah, sorry about that. We normally explicitly test delegations so it inherited our rules. Should be cleaned up now

@TikiTDO TikiTDO requested a review from palkan May 28, 2026 20:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants