diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 9a0dbd686d..7fa7aba642 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -649,10 +649,10 @@ Default: ``None`` The alias of the database that this database should mirror during testing. -This setting exists to allow for testing of master/slave +This setting exists to allow for testing of leader/follower configurations of multiple databases. See the documentation on -:ref:`testing master/slave configurations -` for details. +:ref:`testing leader/follower configurations +` for details. .. setting:: TEST_NAME diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index a5f9b3451c..502b77fbf7 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -222,29 +222,29 @@ won't appear in the models cache, but the model details can be used for routing purposes. For example, the following router would direct all cache read -operations to ``cache_slave``, and all write operations to -``cache_master``. The cache table will only be synchronized onto -``cache_master``:: +operations to ``cache_follower``, and all write operations to +``cache_leader``. The cache table will only be synchronized onto +``cache_leader``:: class CacheRouter(object): """A router to control all database cache operations""" def db_for_read(self, model, **hints): - "All cache read operations go to the slave" + "All cache read operations go to the follower" if model._meta.app_label in ('django_cache',): - return 'cache_slave' + return 'cache_follower' return None def db_for_write(self, model, **hints): - "All cache write operations go to master" + "All cache write operations go to leader" if model._meta.app_label in ('django_cache',): - return 'cache_master' + return 'cache_leader' return None def allow_migrate(self, db, model): - "Only install the cache model on master" + "Only install the cache model on leader" if model._meta.app_label in ('django_cache',): - return db == 'cache_master' + return db == 'cache_leader' return None If you don't specify routing directions for the database cache model, diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt index 470fe1cadb..3dee075e76 100644 --- a/docs/topics/db/multi-db.txt +++ b/docs/topics/db/multi-db.txt @@ -197,17 +197,17 @@ Using routers Database routers are installed using the :setting:`DATABASE_ROUTERS` setting. This setting defines a list of class names, each specifying a -router that should be used by the master router +router that should be used by the leader router (``django.db.router``). -The master router is used by Django's database operations to allocate +The leader router is used by Django's database operations to allocate database usage. Whenever a query needs to know which database to use, -it calls the master router, providing a model and a hint (if +it calls the leader router, providing a model and a hint (if available). Django then tries each router in turn until a database suggestion can be found. If no suggestion can be found, it tries the current ``_state.db`` of the hint instance. If a hint instance wasn't provided, or the instance doesn't currently have database state, the -master router will allocate the ``default`` database. +leader router will allocate the ``default`` database. An example ---------- @@ -225,16 +225,16 @@ An example introduce referential integrity problems that Django can't currently handle. - The master/slave configuration described is also flawed -- it + The leader/follower configuration described is also flawed -- it doesn't provide any solution for handling replication lag (i.e., query inconsistencies introduced because of the time taken for a - write to propagate to the slaves). It also doesn't consider the + write to propagate to the followers). It also doesn't consider the interaction of transactions with the database utilization strategy. So - what does this mean in practice? Let's consider another sample configuration. This one will have several databases: one for the -``auth`` application, and all other apps using a master/slave setup -with two read slaves. Here are the settings specifying these +``auth`` application, and all other apps using a leader/follower setup +with two read followers. Here are the settings specifying these databases:: DATABASES = { @@ -244,20 +244,20 @@ databases:: 'USER': 'mysql_user', 'PASSWORD': 'swordfish', }, - 'master': { - 'NAME': 'master', + 'leader': { + 'NAME': 'leader', 'ENGINE': 'django.db.backends.mysql', 'USER': 'mysql_user', 'PASSWORD': 'spam', }, - 'slave1': { - 'NAME': 'slave1', + 'follower1': { + 'NAME': 'follower1', 'ENGINE': 'django.db.backends.mysql', 'USER': 'mysql_user', 'PASSWORD': 'eggs', }, - 'slave2': { - 'NAME': 'slave2', + 'follower2': { + 'NAME': 'follower2', 'ENGINE': 'django.db.backends.mysql', 'USER': 'mysql_user', 'PASSWORD': 'bacon', @@ -309,30 +309,30 @@ send queries for the ``auth`` app to ``auth_db``:: return None And we also want a router that sends all other apps to the -master/slave configuration, and randomly chooses a slave to read +leader/follower configuration, and randomly chooses a follower to read from:: import random - class MasterSlaveRouter(object): + class LeaderFollowerRouter(object): def db_for_read(self, model, **hints): """ - Reads go to a randomly-chosen slave. + Reads go to a randomly-chosen follower. """ - return random.choice(['slave1', 'slave2']) + return random.choice(['follower1', 'follower2']) def db_for_write(self, model, **hints): """ - Writes always go to master. + Writes always go to leader. """ - return 'master' + return 'leader' def allow_relation(self, obj1, obj2, **hints): """ Relations between objects are allowed if both objects are - in the master/slave pool. + in the leader/follower pool. """ - db_list = ('master', 'slave1', 'slave2') + db_list = ('leader', 'follower1', 'follower2') if obj1._state.db in db_list and obj2._state.db in db_list: return True return None @@ -347,17 +347,17 @@ Finally, in the settings file, we add the following (substituting ``path.to.`` with the actual python path to the module(s) where the routers are defined):: - DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.MasterSlaveRouter'] + DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.LeaderFollowerRouter'] The order in which routers are processed is significant. Routers will be queried in the order the are listed in the :setting:`DATABASE_ROUTERS` setting . In this example, the -``AuthRouter`` is processed before the ``MasterSlaveRouter``, and as a +``AuthRouter`` is processed before the ``LeaderFollowerRouter``, and as a result, decisions concerning the models in ``auth`` are processed before any other decision is made. If the :setting:`DATABASE_ROUTERS` setting listed the two routers in the other order, -``MasterSlaveRouter.allow_migrate()`` would be processed first. The -catch-all nature of the MasterSlaveRouter implementation would mean +``LeaderFollowerRouter.allow_migrate()`` would be processed first. The +catch-all nature of the LeaderFollowerRouter implementation would mean that all models would be available on all databases. With this setup installed, lets run some Django code:: @@ -369,7 +369,7 @@ With this setup installed, lets run some Django code:: >>> # This save will also be directed to 'auth_db' >>> fred.save() - >>> # These retrieval will be randomly allocated to a slave database + >>> # These retrieval will be randomly allocated to a follower database >>> dna = Person.objects.get(name='Douglas Adams') >>> # A new object has no database allocation when created @@ -379,10 +379,10 @@ With this setup installed, lets run some Django code:: >>> # the same database as the author object >>> mh.author = dna - >>> # This save will force the 'mh' instance onto the master database... + >>> # This save will force the 'mh' instance onto the leader database... >>> mh.save() - >>> # ... but if we re-retrieve the object, it will come back on a slave + >>> # ... but if we re-retrieve the object, it will come back on a follower >>> mh = Book.objects.get(title='Mostly Harmless') @@ -690,7 +690,7 @@ In addition, some objects are automatically created just after database). For common setups with multiple databases, it isn't useful to have these -objects in more than one database. Common setups include master / slave and +objects in more than one database. Common setups include leader / follower and connecting to external databases. Therefore, it's recommended: - either to run :djadmin:`migrate` only for the default database; diff --git a/docs/topics/testing/advanced.txt b/docs/topics/testing/advanced.txt index 3439686210..de9c09b919 100644 --- a/docs/topics/testing/advanced.txt +++ b/docs/topics/testing/advanced.txt @@ -64,16 +64,16 @@ The following is a simple unit test using the request factory:: Tests and multiple databases ============================ -.. _topics-testing-masterslave: +.. _topics-testing-leaderfollower: -Testing master/slave configurations +Testing leader/follower configurations ----------------------------------- -If you're testing a multiple database configuration with master/slave +If you're testing a multiple database configuration with leader/follower replication, this strategy of creating test databases poses a problem. When the test databases are created, there won't be any replication, -and as a result, data created on the master won't be seen on the -slave. +and as a result, data created on the leader won't be seen on the +follower. To compensate for this, Django allows you to define that a database is a *test mirror*. Consider the following (simplified) example database @@ -83,34 +83,34 @@ configuration:: 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myproject', - 'HOST': 'dbmaster', + 'HOST': 'dbleader', # ... plus some other settings }, - 'slave': { + 'follower': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'myproject', - 'HOST': 'dbslave', + 'HOST': 'dbfollower', 'TEST_MIRROR': 'default' # ... plus some other settings } } -In this setup, we have two database servers: ``dbmaster``, described -by the database alias ``default``, and ``dbslave`` described by the -alias ``slave``. As you might expect, ``dbslave`` has been configured -by the database administrator as a read slave of ``dbmaster``, so in -normal activity, any write to ``default`` will appear on ``slave``. +In this setup, we have two database servers: ``dbleader``, described +by the database alias ``default``, and ``dbfollower`` described by the +alias ``follower``. As you might expect, ``dbfollower`` has been configured +by the database administrator as a read follower of ``dbleader``, so in +normal activity, any write to ``default`` will appear on ``follower``. If Django created two independent test databases, this would break any -tests that expected replication to occur. However, the ``slave`` +tests that expected replication to occur. However, the ``follower`` database has been configured as a test mirror (using the :setting:`TEST_MIRROR` setting), indicating that under testing, -``slave`` should be treated as a mirror of ``default``. +``follower`` should be treated as a mirror of ``default``. -When the test environment is configured, a test version of ``slave`` -will *not* be created. Instead the connection to ``slave`` +When the test environment is configured, a test version of ``follower`` +will *not* be created. Instead the connection to ``follower`` will be redirected to point at ``default``. As a result, writes to -``default`` will appear on ``slave`` -- but because they are actually +``default`` will appear on ``follower`` -- but because they are actually the same database, not because there is data replication between the two databases. diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index d294bdcd26..b16a6b9073 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -765,7 +765,7 @@ class MultiDBOperationTests(MigrationTestBase): multi_db = True def setUp(self): - # Make the 'other' database appear to be a slave of the 'default' + # Make the 'other' database appear to be a follower of the 'default' self.old_routers = router.routers router.routers = [MigrateNothingRouter()] diff --git a/tests/multiple_database/routers.py b/tests/multiple_database/routers.py index 8a84e0b3d2..23da8df199 100644 --- a/tests/multiple_database/routers.py +++ b/tests/multiple_database/routers.py @@ -4,7 +4,7 @@ from django.db import DEFAULT_DB_ALIAS class TestRouter(object): - # A test router. The behavior is vaguely master/slave, but the + # A test router. The behavior is vaguely leader/follower, but the # databases aren't assumed to propagate changes. def db_for_read(self, model, instance=None, **hints): if instance: diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index 01c6e1ef15..2e8b902fea 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -854,7 +854,7 @@ class QueryTestCase(TestCase): self.assertEqual(book.editor._state.db, 'other') def test_subquery(self): - """Make sure as_sql works with subqueries and master/slave.""" + """Make sure as_sql works with subqueries and leader/follower.""" sub = Person.objects.using('other').filter(name='fff') qs = Book.objects.filter(editor__in=sub) @@ -919,7 +919,7 @@ class RouterTestCase(TestCase): multi_db = True def setUp(self): - # Make the 'other' database appear to be a slave of the 'default' + # Make the 'other' database appear to be a follower of the 'default' self.old_routers = router.routers router.routers = [TestRouter()] @@ -1071,7 +1071,7 @@ class RouterTestCase(TestCase): try: dive.editor = marty except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(marty._state.db, 'default') @@ -1089,7 +1089,7 @@ class RouterTestCase(TestCase): except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') - # This isn't a real master-slave database, so restore the original from other + # This isn't a real leader-follower database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') self.assertEqual(dive._state.db, 'other') @@ -1097,7 +1097,7 @@ class RouterTestCase(TestCase): try: marty.edited = [pro, dive] except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Assignment implies a save, so database assignments of original objects have changed... self.assertEqual(marty._state.db, 'default') @@ -1111,7 +1111,7 @@ class RouterTestCase(TestCase): except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') - # This isn't a real master-slave database, so restore the original from other + # This isn't a real leader-follower database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') self.assertEqual(dive._state.db, 'other') @@ -1119,7 +1119,7 @@ class RouterTestCase(TestCase): try: marty.edited.add(dive) except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Add implies a save, so database assignments of original objects have changed... self.assertEqual(marty._state.db, 'default') @@ -1133,7 +1133,7 @@ class RouterTestCase(TestCase): except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') - # This isn't a real master-slave database, so restore the original from other + # This isn't a real leader-follower database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') # If you assign a FK object when the base object hasn't @@ -1196,7 +1196,7 @@ class RouterTestCase(TestCase): mark = Person.objects.using('default').create(pk=2, name="Mark Pilgrim") # Now save back onto the usual database. - # This simulates master/slave - the objects exist on both database, + # This simulates leader/follower - the objects exist on both database, # but the _state.db is as it is for all other tests. pro.save(using='default') marty.save(using='default') @@ -1213,7 +1213,7 @@ class RouterTestCase(TestCase): try: marty.book_set = [pro, dive] except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') @@ -1232,7 +1232,7 @@ class RouterTestCase(TestCase): try: marty.book_set.add(dive) except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') @@ -1251,7 +1251,7 @@ class RouterTestCase(TestCase): try: dive.authors = [mark, marty] except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') @@ -1273,7 +1273,7 @@ class RouterTestCase(TestCase): try: dive.authors.add(marty) except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') @@ -1311,7 +1311,7 @@ class RouterTestCase(TestCase): try: bob.userprofile = alice_profile except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(alice._state.db, 'default') @@ -1342,7 +1342,7 @@ class RouterTestCase(TestCase): try: review1.content_object = dive except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(pro._state.db, 'default') @@ -1361,7 +1361,7 @@ class RouterTestCase(TestCase): except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') - # This isn't a real master-slave database, so restore the original from other + # This isn't a real leader-follower database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') self.assertEqual(dive._state.db, 'other') @@ -1369,7 +1369,7 @@ class RouterTestCase(TestCase): try: dive.reviews.add(review1) except ValueError: - self.fail("Assignment across master/slave databases with a common source should be ok") + self.fail("Assignment across leader/follower databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(pro._state.db, 'default') @@ -1444,7 +1444,7 @@ class RouterTestCase(TestCase): self.assertEqual(pro.reviews.db_manager('default').all().db, 'default') def test_subquery(self): - """Make sure as_sql works with subqueries and master/slave.""" + """Make sure as_sql works with subqueries and leader/follower.""" # Create a book and author on the other database mark = Person.objects.using('other').create(name="Mark Pilgrim") @@ -1482,7 +1482,7 @@ class AuthTestCase(TestCase): multi_db = True def setUp(self): - # Make the 'other' database appear to be a slave of the 'default' + # Make the 'other' database appear to be a follower of the 'default' self.old_routers = router.routers router.routers = [AuthRouter()]