LEP/FeatureFlags

Not logged in - Log In / Register

Revision 20 as of 2010-07-13 06:58:04

Clear message

The purpose of this template is to help us get ReadyToCode on features or tricky bugs as quickly as possible. See also LaunchpadEnhancementProposalProcess.

Dynamic Configuration

Launchpad has a registry of configuration options that can be changed by admins through the web ui, without restarting Launchpad.

As a Launchpad developer/operator
I want to turn features on and off without a heavyweight deployment
so that I can more adroitly test and deploy new features (A:B testing, long-running closed betas, etc)
and so that I can recover from emergencies by cutting-off problem features

As a Launchpad user
I want you to tell me about impending downtime through the web ui
so that I can I can plan not to be using Launchpad when it's offline/readonly.

Scenarios:

Rationale

Stakeholders

Who really cares about this feature? When did you last talk to them?

Constraints and Requirements

Must

Nice to have

Must not

Subfeatures

Other LaunchpadEnhancementProposals that form a part of this one.

Workflows

What are the workflows for this feature?

Change configuration

LOSA goes to https://launchpad.net/+config where they see a simple web form allowing them to edit the configuration.

Developers can go there to see but not edit the configuration.

Normal users are not allowed to see it.

Provide mockups for each workflow.

Success

How will we know when we are done?

How will we measure how well we have done?

Thoughts?

Put everything else here. Better out than in.

Implementation

The API is: config(name, scopes=None) => value, probably living on a Zope utility.

If the scopes set is not specified, in the web server it is computed from the request object. In other places like jobs or the code host we need to pass in some other object with similar info.

The database model is that there are various "configuration scopes" which each have a name and a total order between them. The order defines the level of specificity: for instance we may have some settings that are active for the edge server, and some for beta user, and say that in case of a conflict the beta user setting has priority. A configuration variable can be defined up to once per configuration scope. Thus to look up the full set of active configuration variables, we look across the selected scopes and take the highest-priority setting.

For any particular scope set it is a single SQL query to get the full environment of settings, something like: select configuration.name, first(value) from configuration natural join configuration_group where group_id in %(scopes)s order by configuration_group.priority group by configuration.name. (Or one can of course query one value at a time.)

The admin gui can show the values grouped and sorted by scope.

We define the following scopes

 100  global
 200  staging_server
 210  edge_server
 220  staging_server
 400  beta_user
 2000 override

Complicated alternative implementations

This should probably live on a zope utility? Is "config" confusable with other names, and if so what should we call it instead?

The flags are named with the same syntax as Python identifiers. All punctuation is reserved so that we can try scope selectors like server=edge/user_group=beta/soyuz_build_from_branch=True.

The value is a Unicode string.

We will add a machine-readable registry of known names, with a help string and a description of the type to be stored in them. (A little like the Mailman admin interface but much simpler.)

The values are stored in a database table, with two columns: name, value. (If we add scope selectors we'll add a third column, so you can quickly pull out all the rows possibly relevant to the name.) This means you perhaps can't change it while we're in readonly mode. Later we can split it to a separate replicated database, or to some non-sql database.

More examples:

The story for how this works: request goes in to the app server code which calls config('bug_page_new'). (Based on this it will choose a different page template or turn on/off some parts of that template.) The config mechanism walks through the configuration settings looking for one that has the name 'bug_page_new' and matches the context. It checks for matches in the context by looking at all the selectors and calling a callable looked up by name. In this case it is 'server' which will look in the request object for the vhost header.

Maybe we don't need multiple levels: if we want things active only on edge for users in ~launchpad-beta, we define a selector function that composes those things.

Or we could eliminate the arguments to the selectors, and just make them simple callables.

Alternative language: put the name first and then the selectors, so that there's exactly one per name:

perhaps we should just use actual Python fragments:

(These could be actually evaluated by Python, or they could just look like Python.)

Or you could put all the logic into the app code and make the config a purely dumb dictionary:

  if user_in_beta or not config('bug_page_new_beta_only') ...

Perhaps the simplest thing would be to say there are several semi-statically-configured scopes, including "edge", "beta users", "everywhere" with a total ordering. We look through these in order for the relevant name. This would mean: