Permalink
Browse files

Releasing multidb from the zamboni womb.

Be free little one!
  • Loading branch information...
0 parents commit a10fa2ddb1e6a7ca28fd02cc3863c688979ed250 @jbalogh committed Feb 9, 2010
Showing with 316 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +27 −0 LICENSE
  3. +2 −0 MANIFEST.in
  4. +27 −0 README.rst
  5. +89 −0 docs/Makefile
  6. +32 −0 docs/conf.py
  7. +8 −0 docs/index.rst
  8. +73 −0 multidb/__init__.py
  9. +28 −0 settings.py
  10. +28 −0 setup.py
@@ -0,0 +1,2 @@
+docs/_build
+.py[co]
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2010, Jeff Balogh.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of multidb-router nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,2 @@
+include LICENSE
+include README.rst
@@ -0,0 +1,27 @@
+With :class:`multidb.MasterSlaveRouter` all read queries will go to a slave
+database; all inserts, updates, and deletes will do to the ``default``
+database.
+
+First, define ``SLAVE_DATABASES`` in your settings. It should be a list of
+database aliases that can be found in ``DATABASES``::
+
+ DATABASES = {
+ 'default': {...},
+ 'shadow-1': {...},
+ 'shadow-2': {...},
+ }
+ SLAVE_DATABASES = ['shadow-1', 'shadow-2']
+
+Then put ``multidb.MasterSlaveRouter`` into DATABASE_ROUTERS::
+
+ DATABASE_ROUTERS = ('multidb.MasterSlaveRouter',)
+
+The slave databases will be chosen in round-robin fashion.
+
+If you want to get a connection to a slave in your app, use
+:func:`multidb.get_slave`::
+
+ from django.db import connections
+ import multidb
+
+ connection = connections[multidb.get_slave()]
@@ -0,0 +1,89 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zamboni.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zamboni.qhc"
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
@@ -0,0 +1,32 @@
+import sys
+import os
+
+sys.path.append(os.path.abspath('..'))
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+extensions = ['sphinx.ext.autodoc']
+
+# General information about the project.
+project = u'multidb-router'
+copyright = u'2010, The Zamboni Collective'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '0.2'
+# The full version, including alpha/beta/rc tags.
+release = '0.2'
+
+# List of directories, relative to source directory, that shouldn't be searched
+# for source files.
+exclude_trees = ['_build']
+
+# The name of the Pygments (syntax highlighting) style to use.
+#pygments_style = 'sphinx'
@@ -0,0 +1,8 @@
+.. _multidb:
+
+=======================
+Master/Slave Databasing
+=======================
+
+.. automodule:: multidb
+ :members:
@@ -0,0 +1,73 @@
+"""
+With :class:`multidb.MasterSlaveRouter` all read queries will go to a slave
+database; all inserts, updates, and deletes will do to the ``default``
+database.
+
+First, define ``SLAVE_DATABASES`` in your settings. It should be a list of
+database aliases that can be found in ``DATABASES``::
+
+ DATABASES = {
+ 'default': {...},
+ 'shadow-1': {...},
+ 'shadow-2': {...},
+ }
+ SLAVE_DATABASES = ['shadow-1', 'shadow-2']
+
+Then put ``multidb.MasterSlaveRouter`` into DATABASE_ROUTERS::
+
+ DATABASE_ROUTERS = ('multidb.MasterSlaveRouter',)
+
+The slave databases will be chosen in round-robin fashion.
+
+If you want to get a connection to a slave in your app, use
+:func:`multidb.get_slave`::
+
+ from django.db import connections
+ import multidb
+
+ connection = connections[multidb.get_slave()]
+"""
+import itertools
+import random
+
+from django.conf import settings
+
+
+DEFAULT_DB_ALIAS = 'default'
+
+
+if getattr(settings, 'SLAVE_DATABASES'):
+ # Shuffle the list so the first slave db isn't slammed during startup.
+ dbs = list(settings.SLAVE_DATABASES)
+ random.shuffle(dbs)
+ slaves = itertools.cycle(dbs)
+ # Set the slaves as test mirrors of the master.
+ for db in dbs:
+ settings.DATABASES[db]['TEST_MIRROR'] = DEFAULT_DB_ALIAS
+else:
+ slaves = itertools.repeat(DEFAULT_DB_ALIAS)
+
+
+def get_slave():
+ """Returns the alias of a slave database."""
+ return slaves.next()
+
+
+class MasterSlaveRouter(object):
+ """Router that sends all reads to a slave, all writes to default."""
+
+ def db_for_read(self, model, **hints):
+ """Send reads to slaves in round-robin."""
+ return get_slave()
+
+ def db_for_write(self, model, **hints):
+ """Send all writes to the master."""
+ return DEFAULT_DB_ALIAS
+
+ def allow_relation(self, obj1, obj2, **hints):
+ """Allow all relations, so FK validation stays quiet."""
+ return True
+
+ def allow_syncdb(self, db, model):
+ """Only allow syncdb on the master."""
+ return db == DEFAULT_DB_ALIAS
@@ -0,0 +1,28 @@
+# An illustrative example settings.py. You need to define DATABASES and
+# SLAVE_DATABASES.
+
+
+# The default database should point to the master.
+DATABASES = {
+ 'default': {
+ 'NAME': 'master',
+ 'ENGINE': 'django.db.backends.mysql',
+ 'HOST': '',
+ 'PORT': '',
+ 'USER': '',
+ 'PASSWORD': '',
+ 'OPTIONS': {'init_command': 'SET storage_engine=InnoDB'},
+ },
+ 'slave': {
+ 'NAME': 'slave',
+ 'ENGINE': 'django.db.backends.mysql',
+ 'HOST': '',
+ 'PORT': '',
+ 'USER': '',
+ 'PASSWORD': '',
+ 'OPTIONS': {'init_command': 'SET storage_engine=InnoDB'},
+ },
+}
+
+# Put the aliases for slave databases in this list.
+SLAVE_DATABASES = ['slave']
@@ -0,0 +1,28 @@
+from setuptools import setup, find_packages
+
+
+setup(
+ name='django-multidb-router',
+ version='0.2',
+ description='Round-robin multidb router for Django 1.2.',
+ long_description=open('README.rst').read(),
+ author='Jeff Balogh',
+ author_email='jbalogh@mozilla.com',
+ url='http://github.com/jbalogh/multidb-router',
+ license='BSD',
+ packages=find_packages(),
+ include_package_data=True,
+ zip_safe=False,
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Web Environment',
+ # I don't know what exactly this means, but why not?
+ 'Environment :: Web Environment :: Mozilla',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ ]
+)

0 comments on commit a10fa2d

Please sign in to comment.