glowworm 0.3.0
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 +15 -0
- data/.gitignore +6 -0
- data/.yardopts +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +129 -0
- data/LICENSE +19 -0
- data/README.md +326 -0
- data/Rakefile +29 -0
- data/bin/basic_server_tester +311 -0
- data/bin/em_server/config.ru +2 -0
- data/bin/em_server/em_server.rb +19 -0
- data/bin/em_server_tester +68 -0
- data/bin/glowworm +90 -0
- data/bin/load_tester +84 -0
- data/ci_jobs/glowworm-continuous-deploy-next-staging/run.sh +10 -0
- data/ci_jobs/glowworm-integrations/run.sh +15 -0
- data/ci_jobs/glowworm-performance/run.sh +2 -0
- data/ci_jobs/glowworm-robustness/run.sh +2 -0
- data/ci_jobs/glowworm-units/run.sh +13 -0
- data/ci_jobs/setup.sh +119 -0
- data/example/example_server.ecology +6 -0
- data/example/example_server.rb +32 -0
- data/glowworm.gemspec +54 -0
- data/lib/glowworm.rb +501 -0
- data/lib/glowworm/em.rb +8 -0
- data/lib/glowworm/no_bg.rb +2 -0
- data/lib/glowworm/version.rb +3 -0
- data/server/Gemfile +27 -0
- data/server/Gemfile.lock +87 -0
- data/server/PROTOCOL +39 -0
- data/server/check_mk_checks/check_glowworm_server +43 -0
- data/server/db_migrations/20111004214649_change_feature_accounts_to_string.rb +60 -0
- data/server/db_migrations/20111028104546_add_value_to_account_set_features.rb +12 -0
- data/server/db_migrations/20120217090636_add_fully_active_flag_to_features.rb +15 -0
- data/server/example_test_data.rb +66 -0
- data/server/glowworm_server.ecology.erb +16 -0
- data/server/glowworm_server.rb +226 -0
- data/server/run/server.sh +7 -0
- data/server/server_test.rb +72 -0
- data/server/version.rb +3 -0
- data/test/integration/basic_server_test.rb +90 -0
- data/test/integration/create_sqlite_data.rb +196 -0
- data/test/integration/em_server_test.rb +68 -0
- data/test/integration/gemfile_for_specific_glowworm_version +17 -0
- data/test/integration/gemfile_for_specific_glowworm_version.lock +55 -0
- data/test/integration/integration_test_helper.rb +153 -0
- data/test/integration/load_test.rb +59 -0
- data/test/integration/nginx.conf +23 -0
- data/test/integration/server_test.ecology.erb +6 -0
- data/test/test_helper.rb +47 -0
- data/test/units/em_test.rb +41 -0
- data/test/units/feature_flag_test.rb +297 -0
- data/test/units/no_bg_test.rb +40 -0
- data/test/units/request_test.rb +51 -0
- 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
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --protected
|
data/Gemfile
ADDED
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.
|