glowworm 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|