glowworm 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +6 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +129 -0
  6. data/LICENSE +19 -0
  7. data/README.md +326 -0
  8. data/Rakefile +29 -0
  9. data/bin/basic_server_tester +311 -0
  10. data/bin/em_server/config.ru +2 -0
  11. data/bin/em_server/em_server.rb +19 -0
  12. data/bin/em_server_tester +68 -0
  13. data/bin/glowworm +90 -0
  14. data/bin/load_tester +84 -0
  15. data/ci_jobs/glowworm-continuous-deploy-next-staging/run.sh +10 -0
  16. data/ci_jobs/glowworm-integrations/run.sh +15 -0
  17. data/ci_jobs/glowworm-performance/run.sh +2 -0
  18. data/ci_jobs/glowworm-robustness/run.sh +2 -0
  19. data/ci_jobs/glowworm-units/run.sh +13 -0
  20. data/ci_jobs/setup.sh +119 -0
  21. data/example/example_server.ecology +6 -0
  22. data/example/example_server.rb +32 -0
  23. data/glowworm.gemspec +54 -0
  24. data/lib/glowworm.rb +501 -0
  25. data/lib/glowworm/em.rb +8 -0
  26. data/lib/glowworm/no_bg.rb +2 -0
  27. data/lib/glowworm/version.rb +3 -0
  28. data/server/Gemfile +27 -0
  29. data/server/Gemfile.lock +87 -0
  30. data/server/PROTOCOL +39 -0
  31. data/server/check_mk_checks/check_glowworm_server +43 -0
  32. data/server/db_migrations/20111004214649_change_feature_accounts_to_string.rb +60 -0
  33. data/server/db_migrations/20111028104546_add_value_to_account_set_features.rb +12 -0
  34. data/server/db_migrations/20120217090636_add_fully_active_flag_to_features.rb +15 -0
  35. data/server/example_test_data.rb +66 -0
  36. data/server/glowworm_server.ecology.erb +16 -0
  37. data/server/glowworm_server.rb +226 -0
  38. data/server/run/server.sh +7 -0
  39. data/server/server_test.rb +72 -0
  40. data/server/version.rb +3 -0
  41. data/test/integration/basic_server_test.rb +90 -0
  42. data/test/integration/create_sqlite_data.rb +196 -0
  43. data/test/integration/em_server_test.rb +68 -0
  44. data/test/integration/gemfile_for_specific_glowworm_version +17 -0
  45. data/test/integration/gemfile_for_specific_glowworm_version.lock +55 -0
  46. data/test/integration/integration_test_helper.rb +153 -0
  47. data/test/integration/load_test.rb +59 -0
  48. data/test/integration/nginx.conf +23 -0
  49. data/test/integration/server_test.ecology.erb +6 -0
  50. data/test/test_helper.rb +47 -0
  51. data/test/units/em_test.rb +41 -0
  52. data/test/units/feature_flag_test.rb +297 -0
  53. data/test/units/no_bg_test.rb +40 -0
  54. data/test/units/request_test.rb +51 -0
  55. metadata +410 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OThhNTlmZjhmMzZmOTRjZmQxMjYwZTZlMzJmNGVmNmEyN2RjODZmYw==
5
+ data.tar.gz: !binary |-
6
+ YTUyZWViZTIwNWY4MmNlNDFkZDgzNzg3MTYzMzkxNGExZmU2MTRiNg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZjkwNDg4ZDYxMDAwMWEwYzU1NzQ1YmZiODgzNmExOTUxZThmYjdhZWUzNTk1
10
+ MmJlMDgxMjIxMWJkNTJiYTg2MTQzOTBjYmM4MjlmZWU2OTQ1MDMwY2RmYzkz
11
+ MWViNDA1ODg3ZGZhYTk0MGQ0YTRjYmQ4ZTYzYzMwZWUxNzg2NDc=
12
+ data.tar.gz: !binary |-
13
+ YjEyYjU3YzU3MTc1ZmY4MjdkMDQ5MWRjMWJmY2RiNTgwZmRjZDQxN2E1ZGVj
14
+ OWQ4YjQ4ZDg1MGUyYjI3MTdiNmU1OTQyODI2ZWZjYjVkZmRiZTcwNTM0NzE5
15
+ NDkxNTcwMjViNmQ5OTk0ODA3MjE3ZjIzM2RkMDJmMzNkZjAyZGU=
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ *~
3
+ test_data.sqlite
4
+ results
5
+ .yardoc/*
6
+ doc/*
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private --protected
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+ source "http://gems.sv2"
3
+
4
+ # Specify your gem's dependencies in your gemspec
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,129 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ glowworm (0.2.6)
5
+ ecology (~> 0.0.18)
6
+ httparty
7
+ multi_json
8
+ termite (~> 0.0.13)
9
+ trollop
10
+
11
+ GEM
12
+ remote: http://rubygems.org/
13
+ remote: http://gems.sv2/
14
+ specs:
15
+ addressable (2.3.2)
16
+ ansi (1.4.3)
17
+ cassandra (0.12.2ooyala2)
18
+ json
19
+ rake
20
+ simple_uuid (~> 0.2.0)
21
+ thrift_client (>= 0.7.0, < 0.9)
22
+ cookiejar (0.3.0)
23
+ daemons (1.1.9)
24
+ ecology (0.0.18)
25
+ erubis
26
+ multi_json
27
+ em-http-request (1.0.3)
28
+ addressable (>= 2.2.3)
29
+ cookiejar
30
+ em-socksify
31
+ eventmachine (>= 1.0.0.beta.4)
32
+ http_parser.rb (>= 0.5.3)
33
+ em-net-http (0.3.7)
34
+ addressable
35
+ em-http-request (>= 0.2.10)
36
+ eventmachine (>= 0.12.10)
37
+ rake
38
+ em-resolv-replace (1.1.3)
39
+ em-socksify (0.2.1)
40
+ eventmachine (>= 1.0.0.beta.4)
41
+ em-synchrony (1.0.2)
42
+ eventmachine (>= 1.0.0.beta.1)
43
+ erubis (2.7.0)
44
+ eventmachine (1.0.0)
45
+ ffi (1.1.5)
46
+ ffi-rzmq (0.9.6)
47
+ ffi
48
+ http_parser.rb (0.5.3)
49
+ httparty (0.11.0)
50
+ multi_json (~> 1.0)
51
+ multi_xml (>= 0.5.2)
52
+ json (1.7.5)
53
+ metaclass (0.0.1)
54
+ minitap (0.3.5)
55
+ minitest
56
+ tapout (>= 0.3.0)
57
+ minitest (3.5.0)
58
+ mocha (0.12.5)
59
+ metaclass (~> 0.0.1)
60
+ multi_json (1.10.1)
61
+ multi_xml (0.5.5)
62
+ mysql (2.8.1)
63
+ nodule (0.0.34)
64
+ cassandra (= 0.12.2ooyala2)
65
+ ffi-rzmq
66
+ rainbow
67
+ rack (1.4.1)
68
+ rack-fiber_pool (0.9.2)
69
+ rack-ooyala-headers (0.1.0)
70
+ rack
71
+ rack-protection (1.2.0)
72
+ rack
73
+ rainbow (1.1.4)
74
+ rake (0.9.2.2)
75
+ scope (0.2.3)
76
+ minitest
77
+ sequel (3.40.0)
78
+ simple_uuid (0.2.0)
79
+ sinatra (1.3.3)
80
+ rack (~> 1.3, >= 1.3.6)
81
+ rack-protection (~> 1.2)
82
+ tilt (~> 1.3, >= 1.3.3)
83
+ sinatra-synchrony (0.4.1)
84
+ em-http-request (~> 1.0)
85
+ em-resolv-replace (~> 1.1)
86
+ em-synchrony (~> 1.0.1)
87
+ eventmachine (~> 1.0.0)
88
+ rack-fiber_pool (~> 0.9)
89
+ sinatra (~> 1.0)
90
+ sqlite3 (1.3.6)
91
+ tapout (0.4.1)
92
+ ansi
93
+ json
94
+ termite (0.0.26)
95
+ ecology (~> 0.0.6)
96
+ multi_json
97
+ rainbow (~> 1.1.3)
98
+ thin (1.5.0)
99
+ daemons (>= 1.0.9)
100
+ eventmachine (>= 0.12.6)
101
+ rack (>= 1.0.0)
102
+ thrift (0.8.0)
103
+ thrift_client (0.8.2)
104
+ thrift (~> 0.8.0)
105
+ tilt (1.3.3)
106
+ trollop (2.0)
107
+ yajl-ruby (1.1.0)
108
+
109
+ PLATFORMS
110
+ ruby
111
+
112
+ DEPENDENCIES
113
+ bundler
114
+ em-net-http
115
+ glowworm!
116
+ minitap (>= 0.3.5)
117
+ mocha
118
+ mysql
119
+ nodule (~> 0.0.25)
120
+ rack-ooyala-headers
121
+ rake
122
+ scope (~> 0.2.1)
123
+ sequel
124
+ sinatra
125
+ sinatra-synchrony
126
+ sqlite3
127
+ tapout
128
+ thin
129
+ yajl-ruby
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Ooyala, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,326 @@
1
+ Glowworm
2
+ ========
3
+
4
+ Glowworm allows you to do gradual rollouts of new features, and "dark
5
+ deploys" -- rolling out code for a feature, then only turning it on
6
+ selectively and after the code is in place everywhere.
7
+
8
+ We call a given feature/account combination a "feature flag". Your
9
+ apps need to know the value of that flag frequently and reliably. But
10
+ you need to change the value fairly quickly when it's time to roll the
11
+ new feature out.
12
+
13
+ Glowworm can make that happen.
14
+
15
+ The name is inspired by dark deploys. First, the code crawls into
16
+ place. When you're ready, it all lights up!
17
+
18
+ Installing
19
+ ==========
20
+
21
+ `gem install glowworm` or use a Gemfile with Bundler.
22
+
23
+ Ooyalans should make sure that "gems.sv2" is listed as a gem source in
24
+ your Gemfile or on your gem command line.
25
+
26
+ Overview
27
+ ========
28
+
29
+ At Ooyala? Want a visual step-by-step version of "how do I use
30
+ Glowworm for a new feature?" We have an online slide deck for that,
31
+ updated from a Glowworm presentation at Ooyala.
32
+ "http://portal.sliderocket.com/BKHPY/Glowworm"
33
+
34
+ If you're not at Ooyala you won't have the nice web interface for
35
+ setting up features, account sets and so on. But the workflow and
36
+ concepts are all the same.
37
+
38
+ Usage
39
+ =====
40
+
41
+ You need to specify an account name or number and a feature name.
42
+ Glowworm handles it from there.
43
+
44
+ if Glowworm.feature_flag("bob's account #", "turn_button_blue?")
45
+ # Code to turn the button blue
46
+ else
47
+ # Code for the default red button
48
+ end
49
+
50
+ You can also prefetch features if you want to:
51
+
52
+ Glowworm.prefetch(:all, :all, :timeout => 3.5)
53
+
54
+ Technically you can supply a feature name or account to prefetch,
55
+ but the current version of Glowworm ignores that and just checks
56
+ the server for all updates.
57
+
58
+ Glowworm features default to false, but begin returning true as you
59
+ turn them on. You can specify a non-boolean value as the default as a
60
+ way of determining if the value is "really" false, or if we're simply
61
+ returning the default.
62
+
63
+ Glowworm.feature_flag("EG-172434", "video_speed", :default => :the_default)
64
+
65
+ In each case where Glowworm can return false, the default will be
66
+ returned instead if you specify one.
67
+
68
+ Lifecycle
69
+ =========
70
+
71
+ When you first start querying a new feature, Glowworm will always
72
+ return false, or your default if you've set one. If the feature
73
+ or account isn't in the database, false is the initial default in
74
+ all cases.
75
+
76
+ You'll need to add the account to the database, add the feature to the
77
+ database, and turn on that feature for that account set. You can see
78
+ an example of code to do this in glowworm/server/example_test_data.rb
79
+ in the Glowworm gem code.
80
+
81
+ Once that has happened, Glowworm should begin returning true for
82
+ that feature.
83
+
84
+ You can also, instead, add an override for that combination of account
85
+ and feature. That's not a particularly scalable way to turn on a
86
+ feature for a large number of accounts in a system with many accounts,
87
+ but it's fine for testing. You can see an example of adding an
88
+ override in example_test_data.rb as well.
89
+
90
+ Options
91
+ =======
92
+
93
+ You can query or prefetch with a TTL or a timeout. The TTL specifies
94
+ how long before Glowworm queries the server about that feature again.
95
+ The timeout specifies how long to wait for a server result before just
96
+ returning a (possibly stale) cached result. 0 is a perfectly good
97
+ timeout or TTL if that's what you need in a given case.
98
+
99
+ # Don't trust cached values, make sure to query the server
100
+ Glowworm.feature_flag("12434", "myfeature", :ttl => 0.0)
101
+
102
+ # Don't wait for a result, give me a stale value but update in the background
103
+ Glowworm.feature_flag("9999", "someFeature", :timeout => 0.0)
104
+
105
+ # Don't wait for a result, give me a stale value and don't update
106
+ Glowworm.feature_flag("9999", "someFeature", :timeout => 0.0, :ttl => 1_000_000)
107
+
108
+ Caching
109
+ =======
110
+
111
+ Glowworm caches locally in memory.
112
+
113
+ Glowworm always queries all accounts and all features from the server
114
+ initially. Then it just exchanges a checksum with the server to find
115
+ out when the data has changed. As soon as the timestamp goes stale,
116
+ the server sends the new information to the client.
117
+
118
+ Configuring with an Ecology
119
+ ===========================
120
+
121
+ Glowworm supports a JSON configuration file managed by the Ecology
122
+ gem. By default it checks the location of the current executable ($0)
123
+ with extension .ecology. So "bob.rb" would have "bob.ecology" next to
124
+ it.
125
+
126
+ Whatever application is using Glowworm will need an Ecology (or to set
127
+ variables explicitly in Ruby) to specify where the Glowworm server is.
128
+ The app can also give options for things like timeout and ttl.
129
+
130
+ An Ecology file has this structure:
131
+
132
+ {
133
+ "application": "MyApp",
134
+ "features": {
135
+ "server": "glowworm.ooyala.com:4999",
136
+ "ttl": "30",
137
+ "timeout": "1000"
138
+ },
139
+ "logging": {
140
+ "console_out": false,
141
+ "default_component": "MyLibrary"
142
+ }
143
+ }
144
+
145
+ Every part is optional, including the presence of the file at all.
146
+ The example above includes extra configuration for termite, another
147
+ Ecology-enabled gem, to show how they combine.
148
+
149
+ The server property gives the hostname and port of the Glowworm
150
+ feature server. If none is specified, glowworm defaults to port 4999
151
+ on localhost. Note that if specifying a server, the port *must* also
152
+ be specified.
153
+
154
+ TTL, if present, gives the number of seconds that a given value is
155
+ considered fresh in the cache. After that time it will be updated.
156
+ This defaults to 5 minutes (300 seconds). Until that time, the cached
157
+ result will be returned. "Refresh" is an outdated name for the same
158
+ setting.
159
+
160
+ Timeout, if present, gives the number of milliseconds to wait when
161
+ querying the server for the correct answer to return. Even if this
162
+ fails the cache will be updated later after the request returns.
163
+
164
+ EventMachine
165
+ ============
166
+
167
+ If you are using glowworm with eventmachine, or in general would not like a background thread,
168
+ then you have a couple of options. In an eventmachine architecture, it is required that your app
169
+ use em-synchrony (or sinatra-synchrony), as well as em-net-http. These are not included in glowworm
170
+ to avoid inclusion of the whole eventmachine stack in the gem. You can require "glowworm/em" to use
171
+ the version which will make em-friendly http calls. This, along with require "glowworm/no_bg" use a
172
+ different version of glowworm that synchronously fetches all data at require time, and otherwise whenever
173
+ Glowworm.update_cache_in_foreground is called. Because of this, you need to be sure your glowworm server
174
+ is properly set before using these requires. One has the additional option of requiring "glowworm", and
175
+ then later in initialization calling Glowworm.no_bg, for non-em apps, or Glowworm.em for eventmachine apps.
176
+ Note that this call to Glowworm.em must be from within a fiber, as it uses EM.synchrony, and could cause your
177
+ app to hang if called from outside eventmachine itself.
178
+
179
+ Servers
180
+ =======
181
+
182
+ An example Glowworm server used by Ooyala is included in the "server"
183
+ directory of the Glowworm gem. The protocol is very simple and you
184
+ should have an easy time implementing a Glowworm server if ours is
185
+ inappropriate for your use case.
186
+
187
+ Our server is nginx serving data from a Sequel-based daemon with an ecology file to
188
+ configure it.
189
+
190
+ To run it, cd into glowworm/server and run `bundle exec ./glowworm_server.rb`
191
+
192
+ You will also need an nginx server serving /opt/ooyala/glowworm/shared/www/ on port 4999.
193
+ You can find the required config file in glowworm/server/config/nginx.conf
194
+
195
+ For basic test data, run `./example_test_data --clear` from the same
196
+ directory.
197
+
198
+ Servers at Ooyala, For Production Use
199
+ =====================================
200
+
201
+ If you're using Glowworm at Ooyala for production features then
202
+ there's more infrastructure to help out.
203
+
204
+ You can create and set flags in production using the Support Tool.
205
+ Start at the URL
206
+ [http://support-tools/features](http://support-tools:3000/features)
207
+ and add or click through to the feature you want. If you can see features
208
+ but can't add or change them, you'll need to talk to the Tools and
209
+ Automation team about getting permissions.
210
+
211
+ You can also create and destroy account sets, add and remove accounts to
212
+ them and set particular features active for particular account sets. See
213
+ [http://support-tools/account_sets](http://support-tools:3000/account_sets).
214
+
215
+ If you want to do the same things in staging rather than in
216
+ production, use the hostname support-tools-staging rather than
217
+ support-tools.
218
+
219
+ Example Application
220
+ ===================
221
+
222
+ In the example subdirectory of the gem you can find a very simple
223
+ Sinatra application that auto-refreshes every five seconds, queries a
224
+ feature on a 20-second TTL and displays a button whose text varies
225
+ according to the feature.
226
+
227
+ First, start your glowworm server (see above). Then start the example
228
+ app server (`cd glowworm/example`, run `bundle exec
229
+ ./example_server.rb`) and then from the server directory run
230
+ `example_test_data --clear --new-signup` and you should see the button
231
+ text change within 25 seconds. Run it with just `--clear`, and you
232
+ should see it change back within another 25 seconds. You can go back
233
+ and forth as often as you have the patience and the browser should
234
+ keep changing.
235
+
236
+ Updating Features and Account Sets
237
+ ==================================
238
+
239
+ The supplied Glowworm server uses a simple set of database tables, and
240
+ includes migrations to set them up. The idea is that you have account
241
+ sets, with a table of accounts in the account sets
242
+ (account_set_accounts). You also have a table of which features each
243
+ account set is true for (account_set_features). Finally, you have a
244
+ table of the features themselves, both to supply names for them and to
245
+ mark each feature fully active (i.e. active for all non-overridden
246
+ accounts). Fully active is only supported in Glowworm versions 0.2.0 and up.
247
+
248
+ Reliability
249
+ ===========
250
+
251
+ In the real world, bad things happen. Sometimes your packets can't
252
+ reach the glowworm server. Sometimes it's down. Sometimes you have
253
+ only very old cached data. Sometimes you have no cached data at all.
254
+ Sometimes the glowworm server is down when your app restarts, so it
255
+ can't load data on startup.
256
+
257
+ So what's the worst case here, and how does Glowworm respond?
258
+
259
+ If Glowworm has already gotten started and the server goes down,
260
+ Glowworm will simply continue returning the same information it last
261
+ saw. When the server comes back up, Glowworm's next poll will return
262
+ better information and fresh information will be provided to the
263
+ application. No problem.
264
+
265
+ If the server is down when Glowworm starts, everything will return
266
+ false. Glowworm will keep trying to query, but until its first
267
+ successful response from the server, everything is false in all cases.
268
+ Even "fully active" features still assume that Glowworm can find out
269
+ about that from the server, so these features return false also.
270
+
271
+ Order of Precedence to determine a Feature's Value
272
+ ==================================================
273
+
274
+ There are two main scenarios in which a feature's value must be determined,
275
+ being if data has been received from the server or not.
276
+
277
+ In the case that we have no data from the server:
278
+ 1) The default set for the Glowworm client (app- or call-level) will be returned.
279
+ 2) If none is set, false will be returned.
280
+
281
+ If the server has been contacted and the caches have been populated:
282
+ 1) If an account override is present, it's value will be returned.
283
+ 2) If an account set has a value set for this feature, it's value will be returned.
284
+ 3) If an app- or call-level default has been set, it's will be returned.
285
+ 4) If the feature has a value set in the "fully_active" field, it's value will be returned.
286
+ 5) If none of these are present, false will be returned.
287
+
288
+ Rate Limits and Scaling
289
+ =======================
290
+
291
+ Glowworm updates are expensive -- all features, all feature sets, all
292
+ overrides and all providers are sent, though not each combination of
293
+ them. However, they're also rare. One update is sent to each client
294
+ when it starts up, and then an update is sent whenever the data
295
+ actually changes on the server. That's comparatively rare.
296
+
297
+ Normally each Glowworm client sends back a checksum from the last
298
+ successful update. If nothing has changed, the server sends back a
299
+ 304 (unchanged) and no further response. The Glowworm client
300
+ considers itself fully up to date, and doesn't poll again until the
301
+ TTL has expired.
302
+
303
+ This makes short TTLs and frequent polling fairly cheap - they require
304
+ a single HTTP exchange with almost no data. However, short timeouts
305
+ are also fine since you don't have to wait for an update to be sure
306
+ it's happening.
307
+
308
+ Design
309
+ ======
310
+
311
+ For its server, database representation and wire protocol, Glowworm
312
+ uses account sets - groups of accounts for which a given feature will
313
+ normally be toggled. It also has individual per-account-and-feature
314
+ override flags, so you don't have to strictly stick with those groups.
315
+
316
+ The account sets are an optimization - we can send which account set
317
+ each account belongs to, and what accounts sets a given feature is
318
+ active for, which lets us send far less data than the full
319
+ accounts-times-features matrix. It's also an excellent user interface
320
+ convention since frequently the same accounts will tend to want the
321
+ earliest, least stable features and want them soonest.
322
+
323
+ If you only set overrides for all your features and don't use account
324
+ sets then you will get much worse performance than Glowworm is
325
+ designed for. Account sets are a significant optimization, not just a
326
+ convenience.