Settings
There are several settings to make for sharding. All of them live in the QUILT_DB variable, except for the router,
which is Django’s own DATABASE_ROUTERS setting, and the settings of the session backend, which are in
QUILT_SESSIONS.
QUILT_DB
MAPPING_MODEL
When you use a mapping model with the @shard_mapping_model decorator,
it is beneficial to add that model’s name to the settings.
If you also add the MappingQuerySet as object manager to that model you can use utils.use_shard_for() to fetch
the wanted shard from mapping table automatically.
# settings
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'MAPPING_MODEL': 'myapp.models.MyMappingModel',
}
# myapp.models
@shard_mapping_model(mapping_field='organization_id')
class OrganizationShard(models.Model):
shard = models.ForeignKey('example.Shard', on_delete=models.CASCADE)
organization_id = models.PositiveSmallIntegerField(db_index=True)
state = models.CharField(choices=STATES, max_length=1, default=State.ACTIVE)
objects = MappingQuerySet.as_manager()
# myapp.views
from djanquiltdb.utils import use_shard_for
with use_shard_for(user.organization_id):
# do things on my shard
Additionally, you can mirror the mapping model by adding @mirrored_model to it.
NEW_SHARD_NODE
Optionally you can tell DjanQuiltDB on which node new shards (schemas) will be created. Without it, every new shard
has to be given its node_name. For example:
DATABASES = {
'default': {'NAME': 'primary', 'ENGINE': 'djanquiltdb.postgresql_backend', ...},
'node_2': {'NAME': 'db_2', 'ENGINE': 'djanquiltdb.postgresql_backend', ...},
}
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'NEW_SHARD_NODE': 'node_2',
}
STATE_EXCEPTION_VIEW and CONNECTION_EXCEPTION_VIEW
The dotted paths of the views that the exception middleware renders for a shard state error and for a database connection error. Without them, the middleware returns a plain 503 response. See Handling unavailable shards.
SESSION_SHARD_SELECTOR_KEY
The session key that UseShardMiddleware and UseShardForMiddleware read the shard selector from. Defaults to
shard_selector, which is the key that the djanquiltdb.sessions backend uses. See Storing the shard selector in the session.
OVERRIDE_SHARDING_MODE
If you want to override the sharding mode for a specific model or application you can use
QUILT_DB['OVERRIDE_SHARDING_MODE'] configuration setting. The setting is a dictionary with tuple or list as a key
and the ShardingMode enum as value. The key is composed from one or two lowercase strings and it is used as a lookup for
an app or a model in an app.
# settings
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'MAPPING_MODEL': 'myapp.models.MyMappingModel',
'OVERRIDE_SHARDING_MODE': {
# The entry overrides app1.ExampleModel sharding_mode to ShardingMode.MIRRORED
('app1', 'examplemodel'): ShardingMode.MIRRORED,
# The entry overrides all app2 models sharding_mode to ShardingMode.SHARDED
('app2',): ShardingMode.SHARDED,
}
}
This section can also be used to set sharding modes to models that no longer exist. Normally the router will look at the model definition to see if a migration for it has to be performed on the public schema or a shard. But if the model is no longer in your code base, it cannot do this anymore. It will mention the definition is missing and skip the migration. If the migration is required, and the model is missing, simply mention it here so the router knows what to do:
# settings
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'MAPPING_MODEL': 'myapp.models.MyMappingModel',
'OVERRIDE_SHARDING_MODE': {
# The entry overrides app1.NonExistentModel sharding_mode to ShardingMode.SHARDED. The model is no
# longer defined, but it might still be mentioned in migrations.
('app1', 'nonexistentmodel'): ShardingMode.SHARDED,
}
}
PRIMARY_DB_ALIAS
Django has the notion of a default database connection. When no routing is given, that connection is used.
In a sharded environment, the idea of a default connection gains new meaning dependent on the type of model
requested:
- Non-decorated
Will only live on the default node’s public schema.
PUBLICTable will exist on all nodes’ public schema. Their data can differ, and all nodes are writable.
MIRROREDTable will exist on all nodes’ public schema. Their data is marked as replicated, so the same across all nodes. Only the
defaultnode is considered writable, the others read-only.SHARDEDTable will exist on all nodes’ sharded schemas. All nodes are writable since their data is unique.
MIRRORED is the interesting situation. In a replicated environment only one node is writable, the others will read
from it and block any mutations of their own.
It is wrong to assume the node that is called default by Django is also always the writable replication node.
These roles can failover and change over time. We cannot change the connection names in the setting to match however.
A node called Rose must always be called Rose, for that name is used to tell which data is on what node.
In order to tell the router which node is writable, we introduce PRIMARY_DB_ALIAS.
Its value is a connection name of the node which is writable for MIRRORED data.
A project that has mirrored models must set it.
DATABASES = {'default': dj_database_url.parse(get_secret('DATABASE_URL'), engine='djanquiltdb.postgresql_backend'),
'other': dj_database_url.parse(get_secret('DATABASE_URL2'), engine='djanquiltdb.postgresql_backend')}
QUILT_DB = {
'PRIMARY_DB_ALIAS': 'default',
'NEW_SHARD_NODE': 'other',
}
And for replication purposes, default is the primary, and other is the follower.
Once this fails over, other becomes primary, and default becomes follower. Their actual names are not to change.
So we alter PRIMARY_DB_ALIAS to tell that other is now writable.
DATABASES = {'default': dj_database_url.parse(get_secret('DATABASE_URL'), engine='djanquiltdb.postgresql_backend'),
'other': dj_database_url.parse(get_secret('DATABASE_URL2'), engine='djanquiltdb.postgresql_backend')}
QUILT_DB = {
'PRIMARY_DB_ALIAS': 'other',
'NEW_SHARD_NODE': 'other',
}
It would be less confusing if we have non-suggestive names for all connections. But Django enforces the existence of
a default node. Even if the router will always route to the primary assigned connection by default.
TEMPLATE_NAME
The name of the template schema each new shard is cloned from. It defaults to template and rarely needs
changing; set it when that name collides with an existing schema in your database.
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'TEMPLATE_NAME': 'shard_template',
}
DATABASE_CREATION_CLASS
The dotted path of the class the backend uses to create and serialize test databases. It defaults to
djanquiltdb.postgresql_backend.creation.DatabaseCreation; point it at a subclass to customize how test
databases are built, for example djanquiltdb.postgresql_backend.creation.TemplateDatabaseCreation.
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'DATABASE_CREATION_CLASS': 'myapp.db.MyDatabaseCreation',
}
MUTE_PGTRIGGER_COMPATIBILITY_WARNING
When django-pgtrigger is installed, DjanQuiltDB checks that it is left in migration mode and reports
djanquiltdb.W001 or djanquiltdb.W002 (see System checks) when it is not. See
Triggers for why the other modes cannot reach every shard. Set this to True to silence
that check; it defaults to False.
QUILT_DB = {
'SHARD_CLASS': 'myapp.models.Shard',
'MUTE_PGTRIGGER_COMPATIBILITY_WARNING': True,
}
DATABASE_ROUTERS
DjanQuiltDB uses a router to send each database transaction to the correct node.
It also uses the router to migrate the models to the correct shard when using ./manage.py migrate.
So set djanquiltdb.router.DynamicDbRouter in DATABASE_ROUTERS in the settings, e.g.:
DATABASE_ROUTERS = ['djanquiltdb.router.DynamicDbRouter']
QUILT_SESSIONS
The settings of the djanquiltdb.sessions session backend. See Storing the shard selector in the session for how to set it up.
SESSION_MODELRequired. The dotted path of the project’s session model, a subclass of
BaseQuiltSession.SHARD_SELECTOR_REGEXThe regular expression that a shard selector value has to match. Defaults to
'[0-9]+'.SESSION_KEY_DELIMITERThe text that separates the shard selector from the rest of a session key. Defaults to
'K', and must be text thatSHARD_SELECTOR_REGEXdoes not match.