Database Model
Balrog’s model centers around two concepts: Rules and Releases. When a request for an update from an application is received it is matched up against the rules. Once the correct rule has been found, it contains a pointer to a Release, which contains all of the metadata needed to construct a proper update response. Rules and Releases are described in greater detail below:
Rules
The most important part of Balrog to understand is its rules. When a request comes in it is matched against Balrog’s rules to find the one that best suits it (more on this in How are requests match to a rule?). Once found, Balrog looks at that rule’s “mapping”, or “fallbackMapping”, which points to a release that has the required information to serve an update back to the client. Without any rules, Balrog will never serve an update. With badly configured rules Balrog could do bad things like serve Firefox updates to Thunderbird users.
What’s in a rule?
Each rule has multiple columns. They all fall into one of the following Category:
Matchable : These correspond to information provided in the update request, and are used to filter out rules that don’t apply to the request
Decision : These are also used to filter rules, but do not correspond to information in the request
Response : these contain information that ends up in the response
Info : Informational columns, not used as part of serving updates
Following tables show columns according to different Categories:
Category
Attribute
Description
Matching Logic
Examples
Decision
backgroundRate
The percentage of background update requests that if specified. Generally, this is used as a throttle to increase or decrease the rate at which the majority of users receive the latest update.
N/A
Any number 0 to 100
Priority
The priority of the rule, relative to other rules. If multiple rules match an incoming request based on the Matchable columns, the rule with the highest priority is chosen.
N/A
Any number, by convention positive integers.
Info
Alias
Optional alias for the rule. Can be used instead of the id in most cases
N/A
“firefox-release-betatest” , “firefox-nightly”
Comment
A string describing the purpose of the rule. Not always necessary for obvious rules.
N/A
Any string
id
The id of the rule. This id is necessary to make changes to the rule through the REST API.
N/A
Autoincrementing integer
Matchable
buildID
The build ID of the application requesting an update.
Exact string match or operator plus buildid to compare the incoming one against
“201410010830” or “<201512010830”
buildTarget
The “build target” of the application requesting an update. This is usually related to the target platform the app was built for.
Exact string match only
“Darwin_x86_64-gcc3- u-i386-x86_64” or “flame-kk-userdebug”
channel
The update channel of the application request an update.
Exact string match or a string with “*” character to glob
“nightly” or “beta*”
distribution
The partner distributions names that the application must send in order for the rule to match or “default” if the application is not a partner build. A comma separated list may be used to list multiple distributions
Exact string match or comma separated list of distributions to do an exact match on
“default”, “yahoo” or “mozilla1, mozilla2”
distVersion
The version of the partner distribution of the application requesting an update or “default” if the application is not a partner build.
Exact string match only
“default” or “1.19”
headerArchitecture
The architecture of the OS of the client as guessed based on build target. This field is mostly deprecated now that this information is included in the build target.
Exact string match only
“PPC” and “Intel” are the only possible values
locale
The locale of the application requesting an update.
Exact string match or comma separated list of locales to do an exact match on
“de” or “en-US,en-GB,id”
osVersion
The OS Version of the application requesting an update. This field is primarily used to point desupported operating systems to their last supported build.
Simplified boolean string match. ‘&&’ ANDs terms while ‘,’ ORs them. Terms are matched using partial strings.
“Windows_NT 5.0” or “Darwin 6,Darwin 7,” or “Windows && (websense-”
product
The name of the application requesting an update.
Exact string match only
“Firefox” or “B2G”
instructionSet
The most modern instruction set supported by the client requesting an update. This field is primarily used to desupport users based on their hardware. Eg: users who do not support SSE2
Full string match or comma separated list of full strings to match on
“SSE” or “MMX,SSE”
memory
The amount of RAM, in megabytes, that the client requesting the update has
Exact match or operator plus memory to compare the incoming one against
“8096” or “<8096” or “>=8096”
jaws
Whether or not the the Rule should apply to queries that indicate a client thas has an incompatible version of the JAWS screen reader installed. If set to True or False the Rule and the query must match precisely.
Exact match only
True, False, or NULL
mig64
Whether or not the Rule should apply to queries that have opted into 32 -> 64-bit migration. If set to True or False the Rule and the query must match precisely.
Exact match only
True, False, or NULL
version
The version of the application requesting an update. Must be at least a two-part version string.
Exact string match or exact matches from list of values or operator plus version to compare the incoming one against
“36.0” or “36.0,36.1,36.2” or “>=38.0a1”
Response
Fallback Mapping
The Release to construct an update out of when the user is on the wrong side of a background rate dice roll. This is a foreign key to the “name” column of the Releases table.
N/A
Any valid release name, or NULL
Mapping
The Release to construct an update out of if the user is on the right side of a background rate dice roll, or if the background rate is 100. This is a foreign key to the “name” column of the Releases table.
N/A
Any valid release name, or NULL
update_type
The update_type to use in the XML response. It’s very rare for a rule to use anything other than “minor” these days.
N/A
“minor” or “major”
How are requests match to a rule?
Most of the Matchable database fields are present as distinct parts of the update URL. For example, most update requests will send a URL in the following format
/update/6/<product>/<version>/<buildID>/<buildTarget>/<locale>/<channel>/<osVersion>/<systemCapabilities>/<distribution>/<distVersion>/update.xml?force=1
There are a few special cases to consider:
systemCapabilities contains comma separated data and breaks down into multple database columns (instructionSet, memory, jaws)
headerArchitecture is extracted from the User-Agent header
mig64 is optional, and comes from the query string instead of the path
The following logic is used to figure out which rule a request matches, and how to respond:
Retrieve all rules where product, buildTarget, distribution, and distVersion are (each) unspecified, or match the request with a simple string match.
Discard any rules where the rule specifies a channel, version, buildID, osVersion, any part of systemCapabilities, and/or locale, and that doesn’t match the request. The method for each match is described in the table above.
The channel has special handling to try “falling back” to a simpler channel, for example a request with release-cck-foo will also consider rules for ‘release’. This only applies to channels containing ‘-cck-‘.
Sort the remaining rules by priority, and keep the one with highest.
The rule’s value for backgroundRate modifies the response
if the request has a query parameter force=1 then the background rate is ignored, and all requests will be served using the release in Mapping
if force is absent then backgroundRate is the percentage of requests which will be served using Mapping
the remaining requests will be served fallbackMapping, if that is specified on the rule, otherwise nothing.
The Release, combined with the update_type specified by the rule, is used to construct an XML response with the details of the update.
Rules example
Rules are usually set up like this, in increasing order of priority:
The lowest priority rule is the main path, providing the latest release for a channel
Special cases are slightly higher, e.g. whatsnew pages for some locales
Watersheds are higher again, to ensure that older release update to the watershed first. The older the watershed the higher the priority, so that X –> Y –> Z is preserved.
The oldest operating system deprecations are highest priority.
Here is a simplified set of rules for Firefox on the release channel, with a throttled main release, a Windows-specific watershed, and the deprecation of Windows 98 along time ago. All other values unspecified, except for update_type being ‘minor’ for all rules.
Priority
Product
Channel
Version
OS Version
Mapping
Fallback Mapping
Background Rate
400
Firefox
release*
Windows_98
No-Update
100
300
Firefox
release
< 43.0.1
Windows_NT
Firefox-43.0.1-build1
100
100
Firefox
release
Firefox-51.0.1-build3
Firefox-50.1.0-build2
25
The first two rules are static, while the last has the two mapping values updated as new releases are created. Future watersheds would be placed with priority below 300, while special cases are closer to 100.
Releases
To Balrog, a “release” is data about a related set of builds. This does _not_ match up with the concept of a “release” being on the “beta”, “release” or “esr” channel elsewhere. In Balrog, each set of nightlies on any branch is considered a release.
While there’s no enforced format on release names, there are a few conventions that we use:
Nightly-style builds submit to releases named by product and branch. Each nightly generally submits to two different releases, one “dated” (eg: Firefox-mozilla-central-nightly-20150513010203) and one “latest” (eg: Firefox-mozilla-central-nightly-latest).
Release-style builds submit to releases named by product, version number, and build number, eg: Firefox-38.0-build1
GMP blobs are created by hand and generally named with the version of each plugin they contain in the name, eg: GMP-20150423-CDM-v4-OpenH264-v1.4
Permissions
The permissions table is a simple list of usernames and the ACLs(Access Control Lists) that they have. A user could be an “admin”, giving them write access to everything, or could have one or more specific permissions. These specific ACLs let us do things such as give B2G folks access to Balrog without the risk of them or their tools accidentally messing up Firefox updates.
The table below describe all possible permissions:
Object
Action
Options
Comments
admin
No supported actions
products - If specified, the user can perform any actions on Rules or Releases that affect the specified products.
An admin user with no options specified has completely unrestricted access to Balrog
rule
create
products - If specified, the user only has permission for the object and action if the changes they are making only affect the product specified.
modify
delete
release
create
modify
delete
release_read_only
set
unset
release_locale
modify
required_signoff
create
modify
delete
permission
create
No supported options.
modify
delete
scheduled_change
enact
No supported options.
Only the Balrog Agent should be granted this permission.
User Roles
Users may hold any number of Roles. Roles are used when signing off on Scheduled Changes.
Roles and Permissions are not directly related - assigning a User a Role does not inherently grant them any Permissions.
Required Signoffs
Some types of changes to Balrog’s database require more than one person to approve them before they can be done. The Required Signoffs tables specify how many signoffs are needed from different Roles for each type of change. For example, a change may required 3 signoffs from users that hold the “releng” Role as well as 1 signoff from a user that holds the “relman” role.
Changes to Required Signoffs tables generally require signoff as well. If you are adding, modifying, or removing signoff requirements for something that already has signoff requirements, you must obtain signoff to do so. For example, if a change requires 2 signoffs from users who hold the “releng” Role, and you want to also require signoff from 1 user who holds the “relman” Role, you must get signoff from 2 “releng” users first. The one exception to this is that if you are adding a new signoff requirement for something that doesn’t require any signoff yet, you do not need any signoff to do so.
You cannot require more signoffs than a Role has users. Eg: if only 3 users hold the “releng” Role, you cannot require 4 “releng” signoffs for anything. Similarly, if 3 “releng” signoffs are currently required for something, and 3 users hold that Role, you cannot remove that Role from any user.
Changes that require signoff will either be Product changes or Permissions changes. Required Signoffs for each are managed independently, and described in more detail below.
Product Required Signoffs
Changes that directly affect updates that clients receive (the Rules and Releases tables) are considered Product changes. Our paranoia level for changes to these varies greatly depending on the Product and Channel. Eg: we’re far more concerned about changes to Firefox’s release channel than we are about Thunderbird’s nightly channel. Because of this, we specify Required Signoffs for these with a product and channel combination.
Any changes to a Rule that would affect a product and channel combination specified in this table will require signoff. This includes Rules that don’t specify a product or channel at all (because that is treated as a wildcard).
Releases which are mapped to be a Rule’s mapping or fallbackingMapping field require the same signoffs as the Rule. Releases that are not mapped to by a rule never require any signoff. It’s important that they are inspected before mapping to them for the first time.
If a change affects more than one product and channel combination, all affected combinations’ required signoffs will be combined to find the full set of required. For example, if Firefox’s release channel requires 3 signoffs from “relman” and Firefox’s beta channel requires 2 signoffs from “releng”, a change to a Rule that affects both channels will require 3 signoffs from “relman” and 2 from “releng”. Changing a Release that is mapped to by Rules on the “release” and “beta” channel would also require the same signoffs.
Permissions Required Signoffs
Changes to the Permissions table may also require signoff. These Required Signoffs are specified by product, which most Permissions support as an option. Changing a Permission that affects the named product wil require the signoffs from the Roles specified in this table. Changing a Permission that does not specify a product will require signoff the signoffs from all Roles specified in this table, because such Permissions grant access to all products. This includes the “admin” Permission and “permission” Permission, which are often used without a product specified.
History Tables
Change attribution and recording is embedded deeply into Balrog. The rules, permissions, required signoffs, and all associated scheduled changes tables have a corresponding history table that records the time a change was made and who made it. This allows us to look back in time when debugging issues, attribute changes to people (aka blame), and quickly roll back bad changes.
Releases History
We also store history for changes to the releases table, but due to its immense size and data type, it is stored in Google Cloud Storage instead of the database. We use two separate buckets for it: one for nightly releases, which has a short expiration window and another for non-nightly releases, which is kept forever.
Scheduled Changes
Some tables (Rules, Releases, and Permissions) support having changes to them scheduled in advance. Tables with Scheduled Changes enabled will have additional related tables to store the necessary information about them.
The primary Scheduled Changes table stores the desired new version of the object and the user who scheduled it. The Conditions table stores information about when to enact the Scheduled Change. Finally, the Signoffs table stores information about who (if anybody) has signed off on the Scheduled Change. All of these tables have their own History tables too.
Permissions for Scheduled Changes are inherited from their asociated base table. Eg: to scheduled a change to a Rule, you must have permission to modify that Rule directly. No special permission is required on top of that.