Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Db routing

Tags:

python

django

I am trying to run my Django application with two db's (1 master, 1 read replica). My problem is if I try to read right after a write the code explodes. For example:

  • p = Product.objects.create()
    1. Product.objects.get(id=p.id)

OR

    1. If user is redirected to Product's details page

The code runs way faster than the read replica. And if the read operation uses the replica the code crashes, because it didn't update in time.

Is there any way to avoid this? For example, the db to read being chosen by request instead of by operation?

My Router is identical to Django's documentation:

import random

class PrimaryReplicaRouter(object):
    def db_for_read(self, model, **hints):
        """
        Reads go to a randomly-chosen replica.
        """
        return random.choice(['replica1', 'replica2'])

    def db_for_write(self, model, **hints):
        """
        Writes always go to primary.
        """
        return 'primary'

    def allow_relation(self, obj1, obj2, **hints):
        """
        Relations between objects are allowed if both objects are
        in the primary/replica pool.
        """
        db_list = ('primary', 'replica1', 'replica2')
        if obj1._state.db in db_list and obj2._state.db in db_list:
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        All non-auth models end up in this pool.
        """
        return True
like image 646
pedrotorres Avatar asked Sep 20 '16 15:09

pedrotorres


3 Answers

Solved it with :

class Model(models.Model): 
    objects = models.Manager()  -> objects only access master
    sobjects = ReplicasManager() -> sobjects access either master and replicas

    class Meta: 
        abstract = True  -> so django doesn't create a table

make every model extend this one instead of models.Model, and then use objects or sobjects whether I want to access only master or if want to access either master or replicas

like image 193
pedrotorres Avatar answered Sep 21 '22 03:09

pedrotorres


Depending on the size of the data and the application I'd tackle this with either of the following methods:

  1. Database pinning:

Extend your database router to allow pinning functions to specific databases. For example:

from customrouter.pinning import use_master

@use_master
def save_and_fetch_foo():
    ...

A good example of that can be seen in django-multidb-router. Of course you could just use this package as well.

  1. Use a model manager to route queries to specific databases.

    class MyManager(models.Manager):
        def get_queryset(self):
            qs = CustomQuerySet(self.model)
            if self._db is not None:
                qs = qs.using(self._db)
            return qs
    
  2. Write a middleware that'd route your requests to master/slave automatically. Basically same as the pinning method but you wouldn't specify when to run GET requests against master.

like image 39
Hevlastka Avatar answered Sep 19 '22 03:09

Hevlastka


IN master replica conf the new data will take few millisecond to replicate the data on all other replica server/database.

so whenever u tried to read after write it wont gives you correct result.

Instead of reading from replica you can use master to read immediately after write by using using('primary') keyword with your get query.

like image 35
sachin Avatar answered Sep 23 '22 03:09

sachin