Skip to content

Commit 6755a03

Browse files
committed
Fixed #12542 -- Added the TEST_MIRROR setting, allowing testing of read slave databases.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12289 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 739d5ae commit 6755a03

File tree

4 files changed

+99
-11
lines changed

4 files changed

+99
-11
lines changed

django/db/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def ensure_defaults(self, alias):
6565
conn.setdefault('TEST_CHARSET', None)
6666
conn.setdefault('TEST_COLLATION', None)
6767
conn.setdefault('TEST_NAME', None)
68+
conn.setdefault('TEST_MIRROR', None)
6869
conn.setdefault('TIME_ZONE', settings.TIME_ZONE)
6970
for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT'):
7071
conn.setdefault(setting, '')

django/test/simple.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -231,16 +231,30 @@ def build_suite(self, test_labels, extra_tests=None):
231231
def setup_databases(self):
232232
from django.db import connections
233233
old_names = []
234+
mirrors = []
234235
for alias in connections:
235236
connection = connections[alias]
236-
old_names.append((connection, connection.settings_dict['NAME']))
237-
connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
238-
return old_names
237+
# If the database is a test mirror, redirect it's connection
238+
# instead of creating a test database.
239+
if connection.settings_dict['TEST_MIRROR']:
240+
mirrors.append((alias, connection))
241+
mirror_alias = connection.settings_dict['TEST_MIRROR']
242+
connections._connections[alias] = connections[mirror_alias]
243+
else:
244+
old_names.append((connection, connection.settings_dict['NAME']))
245+
connection.creation.create_test_db(self.verbosity, autoclobber=not self.interactive)
246+
return old_names, mirrors
239247

240248
def run_suite(self, suite):
241249
return DjangoTestRunner(verbosity=self.verbosity, failfast=self.failfast).run(suite)
242250

243-
def teardown_databases(self, old_names):
251+
def teardown_databases(self, old_config):
252+
from django.db import connections
253+
old_names, mirrors = old_config
254+
# Point all the mirrors back to the originals
255+
for alias, connection in mirrors:
256+
connections._connections[alias] = connection
257+
# Destroy all the non-mirror databases
244258
for connection, old_name in old_names:
245259
connection.creation.destroy_test_db(old_name, self.verbosity)
246260

@@ -273,11 +287,11 @@ def run_tests(self, test_labels, extra_tests=None):
273287

274288
suite = self.build_suite(test_labels, extra_tests)
275289

276-
old_names = self.setup_databases()
290+
old_config = self.setup_databases()
277291

278292
result = self.run_suite(suite)
279293

280-
self.teardown_databases(old_names)
294+
self.teardown_databases(old_config)
281295

282296
self.teardown_test_environment()
283297

docs/ref/settings.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,21 @@ Only supported for the ``mysql`` backend (see the `MySQL manual`_ for details).
357357

358358
.. _MySQL manual: MySQL_
359359

360+
.. setting:: TEST_MIRROR
361+
362+
TEST_MIRROR
363+
~~~~~~~~~~~
364+
365+
Default: ``None``
366+
367+
The alias of the database that this database should mirror during
368+
testing.
369+
370+
This setting exists to allow for testing of master/slave
371+
configurations of multiple databases. See the documentation on
372+
:ref:`testing master/slave configurations
373+
<topics-testing-masterslave>` for details.
374+
360375
.. setting:: TEST_NAME
361376

362377
TEST_NAME

docs/topics/testing.txt

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,12 @@ Regardless of whether the tests pass or fail, the test databases are destroyed
301301
when all the tests have been executed.
302302

303303
By default the test databases get their names by prepending ``test_``
304-
to the value of the :setting:`NAME`` settings for the databased
304+
to the value of the :setting:`NAME` settings for the databases
305305
defined in :setting:`DATABASES`. When using the SQLite database engine
306306
the tests will by default use an in-memory database (i.e., the
307307
database will be created in memory, bypassing the filesystem
308308
entirely!). If you want to use a different database name, specify
309-
``TEST_NAME`` in the dictionary for any given database in
309+
:setting:`TEST_NAME` in the dictionary for any given database in
310310
:setting:`DATABASES`.
311311

312312
Aside from using a separate database, the test runner will otherwise
@@ -325,6 +325,58 @@ control the particular collation used by the test database. See the
325325
:ref:`settings documentation <ref-settings>` for details of these
326326
advanced settings.
327327

328+
.. _topics-testing-masterslave:
329+
330+
Testing master/slave configurations
331+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
332+
333+
.. versionadded:: 1.2
334+
335+
If you're testing a multiple database configuration with master/slave
336+
replication, this strategy of creating test databases poses a problem.
337+
When the test databases are created, there won't be any replication,
338+
and as a result, data created on the master won't be seen on the
339+
slave.
340+
341+
To compensate for this, Django allows you to define that a database is
342+
a *test mirror*. Consider the following (simplified) example database
343+
configuration::
344+
345+
DATABASES = {
346+
'default': {
347+
'ENGINE': 'django.db.backends.mysql',
348+
'NAME': 'myproject',
349+
'HOST': 'dbmaster',
350+
# ... plus some other settings
351+
},
352+
'slave': {
353+
'ENGINE': 'django.db.backends.mysql',
354+
'NAME': 'myproject',
355+
'HOST': 'dbslave',
356+
'TEST_MIRROR': 'default'
357+
# ... plus some other settings
358+
}
359+
}
360+
361+
In this setup, we have two database servers: ``dbmaster``, described
362+
by the database alias ``default``, and ``dbslave`` described by the
363+
alias ``slave``. As you might expect, ``dbslave`` has been configured
364+
by the database administrator as a read slave of ``dbmaster``, so in
365+
normal activity, any write to ``default`` will appear on ``slave``.
366+
367+
If Django created two independent test databases, this would break any
368+
tests that expected replication to occur. However, the ``slave``
369+
database has been configured as a test mirror (using the
370+
:setting:`TEST_MIRROR` setting), indicating that under testing,
371+
``slave`` should be treated as a mirror of ``default``.
372+
373+
When the test environment is configured, a test version of ``slave``
374+
will *not* be created. Instead the connection to ``slave``
375+
will be redirected to point at ``default``. As a result, writes to
376+
``default`` will appear on ``slave`` -- but because they are actually
377+
the same database, not because there is data replication between the
378+
two databases.
379+
328380
Other test conditions
329381
---------------------
330382

@@ -1349,17 +1401,23 @@ set up, execute and tear down the test suite.
13491401

13501402
Creates the test databases.
13511403

1352-
Returns the list of old database names that will need to be restored
1404+
Returns a data structure that provides enough detail to undo the changes
1405+
that have been made. This data will be provided to the ``teardown_databases()``
1406+
function at the conclusion of testing.
13531407

13541408
.. method:: DjangoTestSuiteRunner.run_suite(suite)
13551409

13561410
Runs the test suite.
13571411

13581412
Returns the result produced by the running the test suite.
13591413

1360-
.. method:: DjangoTestSuiteRunner.teardown_databases(old_names)
1414+
.. method:: DjangoTestSuiteRunner.teardown_databases(old_config)
1415+
1416+
Destroys the test databases, restoring pre-test conditions.
13611417

1362-
Destroys the test databases, restoring the old names.
1418+
``old_config`` is a data structure defining the changes in the
1419+
database configuration that need to be reversed. It is the return
1420+
value of the ``setup_databases()`` method.
13631421

13641422
.. method:: DjangoTestSuiteRunner.teardown_test_environment()
13651423

0 commit comments

Comments
 (0)