graphql-anycable 1.1.0 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +12 -33
- data/CHANGELOG.md +21 -0
- data/Gemfile +1 -0
- data/README.md +36 -3
- data/graphql-anycable.gemspec +4 -1
- data/lib/graphql/anycable/version.rb +1 -1
- data/lib/graphql/subscriptions/anycable_subscriptions.rb +38 -17
- metadata +52 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe8111474650b5580715d08c4122d70db3dcf45a4e1760d561e05119124181f7
|
4
|
+
data.tar.gz: 02e62b9ab477143af5889de901181de56aab1d88ec6a174041f4a2307090bbb0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ff1cfaa560fc9361704545d6c071658e8847d07b8d37f72b2680396046788f5c65ce3d63a026b1af10192a96684a54e078d1b7a5572e13bf778d52fd1e5fd53
|
7
|
+
data.tar.gz: bc67b3fb5b20f6e0ec06637b40410ecdd6420d58f7cb4eb3eaa610a4258fda1139d2cfd24237280e1af74893c06a363b6e6611ae5c0146439c4d9094cb07b0c6
|
data/.github/workflows/test.yml
CHANGED
@@ -10,50 +10,31 @@ on:
|
|
10
10
|
|
11
11
|
jobs:
|
12
12
|
test:
|
13
|
-
name: "GraphQL-Ruby ${{ matrix.graphql }}
|
13
|
+
name: "GraphQL-Ruby ${{ matrix.graphql }} on Ruby ${{ matrix.ruby }} (use_client_id: ${{ matrix.client_id }})"
|
14
14
|
runs-on: ubuntu-latest
|
15
15
|
strategy:
|
16
16
|
fail-fast: false
|
17
17
|
matrix:
|
18
18
|
include:
|
19
|
+
- ruby: "3.1"
|
20
|
+
graphql: '~> 2.0.0'
|
21
|
+
client_id: 'false'
|
22
|
+
anycable_rails: '~> 1.3'
|
19
23
|
- ruby: "3.0"
|
20
|
-
graphql: '~> 1.
|
21
|
-
|
22
|
-
|
23
|
-
- ruby: "3.0"
|
24
|
-
graphql: '~> 1.12.0'
|
25
|
-
anycable: '~> 1.1.0'
|
26
|
-
interpreter: no
|
27
|
-
- ruby: 2.7
|
28
|
-
graphql: '~> 1.12.0'
|
29
|
-
anycable: '~> 1.1.0'
|
30
|
-
interpreter: yes
|
24
|
+
graphql: '~> 1.13.0'
|
25
|
+
client_id: 'false'
|
26
|
+
anycable_rails: '~> 1.2.0'
|
31
27
|
- ruby: 2.7
|
32
28
|
graphql: '~> 1.12.0'
|
33
|
-
|
34
|
-
|
35
|
-
- ruby: 2.6
|
36
|
-
graphql: '~> 1.11.0'
|
37
|
-
anycable: '~> 1.0.0'
|
38
|
-
interpreter: yes
|
39
|
-
- ruby: 2.6
|
40
|
-
graphql: '~> 1.11.0'
|
41
|
-
anycable: '~> 1.0.0'
|
42
|
-
interpreter: no
|
43
|
-
- ruby: 2.5
|
44
|
-
graphql: '~> 1.11.0'
|
45
|
-
anycable: '~> 1.0.0'
|
46
|
-
interpreter: yes
|
47
|
-
- ruby: 2.5
|
48
|
-
graphql: '~> 1.11.0'
|
49
|
-
anycable: '~> 1.0.0'
|
50
|
-
interpreter: no
|
29
|
+
client_id: 'true'
|
30
|
+
anycable_rails: '~> 1.1.0'
|
51
31
|
container:
|
52
32
|
image: ruby:${{ matrix.ruby }}
|
53
33
|
env:
|
54
34
|
CI: true
|
55
35
|
GRAPHQL_RUBY_VERSION: ${{ matrix.graphql }}
|
56
|
-
|
36
|
+
ANYCABLE_RAILS_VERSION: ${{ matrix.anycable_rails }}
|
37
|
+
GRAPHQL_ANYCABLE_USE_CLIENT_PROVIDED_UNIQ_ID: ${{ matrix.client_id }}
|
57
38
|
steps:
|
58
39
|
- uses: actions/checkout@v2
|
59
40
|
- uses: actions/cache@v2
|
@@ -71,6 +52,4 @@ jobs:
|
|
71
52
|
bundle install
|
72
53
|
bundle update
|
73
54
|
- name: Run RSpec
|
74
|
-
env:
|
75
|
-
GRAPHQL_RUBY_INTERPRETER: ${{ matrix.interpreter }}
|
76
55
|
run: bundle exec rspec
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
7
7
|
|
8
8
|
## Unreleased
|
9
9
|
|
10
|
+
## 1.1.3 - 2022-03-11
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Allow using graphql-anycable with GraphQL-Ruby 2.x (it seem to be already compatible). [@Envek]
|
15
|
+
|
16
|
+
## 1.1.2 - 2022-03-11
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
- AnyCable 1.3.0 compatibility. [@palkan] [#21](https://github.com/anycable/graphql-anycable/pull/21)
|
21
|
+
- Redis.rb 5.0 compatibility. [@palkan] [#21](https://github.com/anycable/graphql-anycable/pull/21)
|
22
|
+
|
23
|
+
## 1.1.1 - 2021-12-06
|
24
|
+
|
25
|
+
### Fixed
|
26
|
+
|
27
|
+
- Handling of buggy istate values on unsubscribe (when `use_client_provided_uniq_id: false`). [@palkan] [#20](https://github.com/anycable/graphql-anycable/pull/20)
|
28
|
+
- A bug when `#unsubscribe` happens before `#execute`. [@palkan] [#20](https://github.com/anycable/graphql-anycable/pull/20)
|
29
|
+
|
10
30
|
## 1.1.0 - 2021-11-17
|
11
31
|
|
12
32
|
### Added
|
@@ -142,3 +162,4 @@ Initial version: store subscriptions on redis, re-execute queries in sync. [@Env
|
|
142
162
|
[@bibendi]: https://github.com/bibendi "Misha Merkushin"
|
143
163
|
[@FX-HAO]: https://github.com/FX-HAO "Fuxin Hao"
|
144
164
|
[@Envek]: https://github.com/Envek "Andrey Novikov"
|
165
|
+
[@palkan]: https://github.com/palkan "Vladimir Dementyev"
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -111,8 +111,8 @@ By default all fields are marked as _not safe for broadcasting_. If a subscripti
|
|
111
111
|
|
112
112
|
```ruby
|
113
113
|
class MySchema < GraphQL::Schema
|
114
|
-
use GraphQL::Execution::Interpreter # Required for graphql-ruby before 1.12.
|
115
|
-
use GraphQL::Analysis::AST
|
114
|
+
use GraphQL::Execution::Interpreter # Required for graphql-ruby before 1.12. Remove it when upgrading to 2.0
|
115
|
+
use GraphQL::Analysis::AST # Required for graphql-ruby before 1.12. Remove it when upgrading to 2.0
|
116
116
|
use GraphQL::AnyCable, broadcast: true, default_broadcastable: true
|
117
117
|
|
118
118
|
subscription SubscriptionType
|
@@ -144,7 +144,7 @@ GraphQL-AnyCable uses [anyway_config] to configure itself. There are several pos
|
|
144
144
|
GRAPHQL_ANYCABLE_USE_CLIENT_PROVIDED_UNIQ_ID=false
|
145
145
|
```
|
146
146
|
|
147
|
-
2. YAML configuration files:
|
147
|
+
2. YAML configuration files (note that this is `config/graphql_anycable.yml`, *not* `config/anycable.yml`):
|
148
148
|
|
149
149
|
```yaml
|
150
150
|
# config/graphql_anycable.yml
|
@@ -215,6 +215,39 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
|
215
215
|
|
216
216
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
217
217
|
|
218
|
+
### Releasing new versions
|
219
|
+
|
220
|
+
1. Bump version number in `lib/graphql/anycable/version.rb`
|
221
|
+
|
222
|
+
In case of pre-releases keep in mind [rubygems/rubygems#3086](https://github.com/rubygems/rubygems/issues/3086) and check version with command like `Gem::Version.new(AfterCommitEverywhere::VERSION).to_s`
|
223
|
+
|
224
|
+
2. Fill `CHANGELOG.md` with missing changes, add header with version and date.
|
225
|
+
|
226
|
+
3. Make a commit:
|
227
|
+
|
228
|
+
```sh
|
229
|
+
git add lib/graphql/anycable/version.rb CHANGELOG.md
|
230
|
+
version=$(ruby -r ./lib/graphql/anycable/version.rb -e "puts Gem::Version.new(GraphQL::AnyCable::VERSION)")
|
231
|
+
git commit --message="${version}: " --edit
|
232
|
+
```
|
233
|
+
|
234
|
+
4. Create annotated tag:
|
235
|
+
|
236
|
+
```sh
|
237
|
+
git tag v${version} --annotate --message="${version}: " --edit --sign
|
238
|
+
```
|
239
|
+
|
240
|
+
5. Fill version name into subject line and (optionally) some description (list of changes will be taken from `CHANGELOG.md` and appended automatically)
|
241
|
+
|
242
|
+
6. Push it:
|
243
|
+
|
244
|
+
```sh
|
245
|
+
git push --follow-tags
|
246
|
+
```
|
247
|
+
|
248
|
+
7. GitHub Actions will create a new release, build and push gem into [rubygems.org](https://rubygems.org)! You're done!
|
249
|
+
|
250
|
+
|
218
251
|
## Contributing
|
219
252
|
|
220
253
|
Bug reports and pull requests are welcome on GitHub at https://github.com/Envek/graphql-anycable.
|
data/graphql-anycable.gemspec
CHANGED
@@ -28,11 +28,14 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency "anycable", "~> 1.0"
|
30
30
|
spec.add_dependency "anyway_config", ">= 1.3", "< 3"
|
31
|
-
spec.add_dependency "graphql", "
|
31
|
+
spec.add_dependency "graphql", ">= 1.11", "< 3"
|
32
32
|
spec.add_dependency "redis", ">= 4.2.0"
|
33
33
|
|
34
|
+
spec.add_development_dependency "anycable-rails"
|
34
35
|
spec.add_development_dependency "bundler", "~> 2.0"
|
35
36
|
spec.add_development_dependency "fakeredis"
|
37
|
+
spec.add_development_dependency "rack"
|
38
|
+
spec.add_development_dependency "railties"
|
36
39
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
37
40
|
spec.add_development_dependency "rspec", "~> 3.0"
|
38
41
|
end
|
@@ -79,9 +79,9 @@ module GraphQL
|
|
79
79
|
return if fingerprints.empty?
|
80
80
|
|
81
81
|
fingerprint_subscription_ids = Hash[fingerprints.zip(
|
82
|
-
redis.pipelined do
|
82
|
+
redis.pipelined do |pipeline|
|
83
83
|
fingerprints.map do |fingerprint|
|
84
|
-
|
84
|
+
pipeline.smembers(SUBSCRIPTIONS_PREFIX + fingerprint)
|
85
85
|
end
|
86
86
|
end
|
87
87
|
)]
|
@@ -160,16 +160,16 @@ module GraphQL
|
|
160
160
|
events: events.map { |e| [e.topic, e.fingerprint] }.to_h.to_json,
|
161
161
|
}
|
162
162
|
|
163
|
-
redis.multi do
|
164
|
-
|
165
|
-
|
163
|
+
redis.multi do |pipeline|
|
164
|
+
pipeline.sadd(CHANNEL_PREFIX + channel_uniq_id, subscription_id)
|
165
|
+
pipeline.mapped_hmset(SUBSCRIPTION_PREFIX + subscription_id, data)
|
166
166
|
events.each do |event|
|
167
|
-
|
168
|
-
|
167
|
+
pipeline.zincrby(FINGERPRINTS_PREFIX + event.topic, 1, event.fingerprint)
|
168
|
+
pipeline.sadd(SUBSCRIPTIONS_PREFIX + event.fingerprint, subscription_id)
|
169
169
|
end
|
170
170
|
next unless config.subscription_expiration_seconds
|
171
|
-
|
172
|
-
|
171
|
+
pipeline.expire(CHANNEL_PREFIX + channel_uniq_id, config.subscription_expiration_seconds)
|
172
|
+
pipeline.expire(SUBSCRIPTION_PREFIX + subscription_id, config.subscription_expiration_seconds)
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
@@ -191,19 +191,19 @@ module GraphQL
|
|
191
191
|
events = redis.hget(SUBSCRIPTION_PREFIX + subscription_id, :events)
|
192
192
|
events = events ? JSON.parse(events) : {}
|
193
193
|
fingerprint_subscriptions = {}
|
194
|
-
redis.pipelined do
|
194
|
+
redis.pipelined do |pipeline|
|
195
195
|
events.each do |topic, fingerprint|
|
196
|
-
|
197
|
-
score =
|
196
|
+
pipeline.srem(SUBSCRIPTIONS_PREFIX + fingerprint, subscription_id)
|
197
|
+
score = pipeline.zincrby(FINGERPRINTS_PREFIX + topic, -1, fingerprint)
|
198
198
|
fingerprint_subscriptions[FINGERPRINTS_PREFIX + topic] = score
|
199
199
|
end
|
200
200
|
# Delete subscription itself
|
201
|
-
|
201
|
+
pipeline.del(SUBSCRIPTION_PREFIX + subscription_id)
|
202
202
|
end
|
203
203
|
# Clean up fingerprints that doesn't have any subscriptions left
|
204
|
-
redis.pipelined do
|
204
|
+
redis.pipelined do |pipeline|
|
205
205
|
fingerprint_subscriptions.each do |key, score|
|
206
|
-
|
206
|
+
pipeline.zremrangebyscore(key, '-inf', '0') if score.value.zero?
|
207
207
|
end
|
208
208
|
end
|
209
209
|
delete_legacy_subscription(subscription_id)
|
@@ -225,6 +225,10 @@ module GraphQL
|
|
225
225
|
def delete_channel_subscriptions(channel_or_id)
|
226
226
|
# For backward compatibility
|
227
227
|
channel_id = channel_or_id.is_a?(String) ? channel_or_id : read_subscription_id(channel_or_id)
|
228
|
+
|
229
|
+
# Missing in case disconnect happens before #execute
|
230
|
+
return unless channel_id
|
231
|
+
|
228
232
|
redis.smembers(CHANNEL_PREFIX + channel_id).each do |subscription_id|
|
229
233
|
delete_subscription(subscription_id)
|
230
234
|
end
|
@@ -240,13 +244,30 @@ module GraphQL
|
|
240
244
|
def read_subscription_id(channel)
|
241
245
|
return channel.instance_variable_get(:@__sid__) if channel.instance_variable_defined?(:@__sid__)
|
242
246
|
|
243
|
-
|
247
|
+
istate = fetch_channel_istate(channel)
|
248
|
+
|
249
|
+
return unless istate
|
250
|
+
|
251
|
+
channel.instance_variable_set(:@__sid__, istate["sid"])
|
244
252
|
end
|
245
253
|
|
246
254
|
def write_subscription_id(channel, val)
|
247
|
-
channel.connection.
|
255
|
+
channel.connection.anycable_socket.istate["sid"] = val
|
248
256
|
channel.instance_variable_set(:@__sid__, val)
|
249
257
|
end
|
258
|
+
|
259
|
+
def fetch_channel_istate(channel)
|
260
|
+
# For Rails integration
|
261
|
+
return channel.__istate__ if channel.respond_to?(:__istate__)
|
262
|
+
|
263
|
+
return unless channel.connection.socket.istate
|
264
|
+
|
265
|
+
if channel.connection.socket.istate[channel.identifier]
|
266
|
+
JSON.parse(channel.connection.socket.istate[channel.identifier])
|
267
|
+
else
|
268
|
+
channel.connection.socket.istate
|
269
|
+
end
|
270
|
+
end
|
250
271
|
end
|
251
272
|
end
|
252
273
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-anycable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrey Novikov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: anycable
|
@@ -48,16 +48,22 @@ dependencies:
|
|
48
48
|
name: graphql
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - "
|
51
|
+
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '1.11'
|
54
|
+
- - "<"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '3'
|
54
57
|
type: :runtime
|
55
58
|
prerelease: false
|
56
59
|
version_requirements: !ruby/object:Gem::Requirement
|
57
60
|
requirements:
|
58
|
-
- - "
|
61
|
+
- - ">="
|
59
62
|
- !ruby/object:Gem::Version
|
60
63
|
version: '1.11'
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '3'
|
61
67
|
- !ruby/object:Gem::Dependency
|
62
68
|
name: redis
|
63
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,6 +78,20 @@ dependencies:
|
|
72
78
|
- - ">="
|
73
79
|
- !ruby/object:Gem::Version
|
74
80
|
version: 4.2.0
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: anycable-rails
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
75
95
|
- !ruby/object:Gem::Dependency
|
76
96
|
name: bundler
|
77
97
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,6 +120,34 @@ dependencies:
|
|
100
120
|
- - ">="
|
101
121
|
- !ruby/object:Gem::Version
|
102
122
|
version: '0'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: rack
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: railties
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
type: :development
|
145
|
+
prerelease: false
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - ">="
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
103
151
|
- !ruby/object:Gem::Dependency
|
104
152
|
name: rake
|
105
153
|
requirement: !ruby/object:Gem::Requirement
|