LINUX.ORG.RU

Несколько баз данных в Django

 ,


2

4

Доброго дня комрады! Настраиваю связку nginx+gunicorn+memcached+postgre На данный момент уперся в использование 2х БД по принципу: Если одна не доступна использовать вторую. в setting.py

DATABASES = {
    'default': {},
        'users1': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'gunicorn',
        'USER': 'gunicorn',
        'PASSWORD': 'gunicorn',
        'HOST': '10.230.40.194',
        'PORT': '5432',
    },
        'users': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'gunicorn',
        'USER': 'gunicorn',
        'PASSWORD': 'gunicorn',
        'HOST': '10.230.40.195',
        'PORT': '5432',
        }
}
DATABASE_ROUTERS = ["db_router.ProjectDbRouter"]

Далее в корне проекта нужно создать project_db_router.py Но как его правильно заполнить для моей задачи я не знаю, ибо в программировании нуб, что печально. Вот вырезка с оф сайта

class AuthRouter(object):
    """
    A router to control all database operations on models in the
    auth application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.app_label == 'auth':
            return 'auth_db'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        if model._meta.app_label == 'auth':
            return 'auth_db'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the auth app is involved.
        """
        if obj1._meta.app_label == 'auth' or \
           obj2._meta.app_label == 'auth':
           return True
        return None

    def allow_syncdb(self, db, model):
        """
        Make sure the auth app only appears in the 'auth_db'
        database.
        """
        if db == 'auth_db':
            return model._meta.app_label == 'auth'
        elif model._meta.app_label == 'auth':
            return False
        return None
Подскажите как правильно класс для моей задачи описать? Можно было бы методом тыка, но боюсь это потом плохо отразится на проекте. И еще попутно вопрос, присоеденил memcached по мануалу, но python ругается на синтаксис, что не правильно то?
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
        'LOCATION': ['10.230.40.192:11211', '10.230.40.193:11211',]
       'OPTIONS': {
            'MAX_ENTRIES': 1000,
               'TIMEOUT': 60,
        }
    }
}
Ругань идет на 'OPTIONS' 'MAX_ENTRIES' 'TIMEOUT'

Но как его правильно заполнить для моей задачи я не знаю, ибо в программировании нуб, что печально

https://stackoverflow.com/questions/26608906/django-multiple-databases-fallba...

Обрати внимание: тут запись идёт строго в одну и ту же базу. Иначе ты рискуешь откатить записанные данные после того, как вторая база поднимется.

x3al ★★★★★ ()
Ответ на: комментарий от x3al

А, ещё одна деталь. С таким конфигом если у тебя дефолтная база для чтения и записи разные — чтение сразу после записи может вернуть устаревшие данные если запись не ждёт, пока данные расползутся по репликам.

x3al ★★★★★ ()
Последнее исправление: x3al (всего исправлений: 1)

'LOCATION': ['10.230.40.192:11211', '10.230.40.193:11211',]

Запятую забыл в конце строки.

gruy ★★★ ()

А дублирование и контроль работы БД лучше возложить на специализированные средства, потому что тебе иначе придется решать много проблем с синхронизацией данных и т.п.

gruy ★★★ ()
Ответ на: комментарий от gruy

Спасибо за запятую. Проглядел. Что можете посоветовать в качестве специализированных средств? Идея в целом такая

ASA+LB
|
|
|------nginx1---gunicorn--memcached---pgsql
|---|---|--\------/--\------/--\------/ |
|---|---FS--\----/----\----/----\----/--|
|---|---|----\--/------\--/------\--/---|
|---|--sync2--\/--------\/--------\/---sync
|---|---|-----/\--------/\--------/\----|
|---|---FS---/--\------/--\------/--\---|
|---|---|---/----\----/----\----/----\--|
|------nginx2--gunicorn--memcached---pgsql
При выходе из строя любого сервера его всегда заменит дублирующий.Каким образом сказать gunicorn что база в его «горизонтальной» цепочке вышла из строя, юзай «диагональную» связь?

Demolitionman ()
Ответ на: комментарий от Demolitionman

Ты пытаешься изобрести репликацию. Могу огорчить - она уже изобретена.

nikolnik ★★★ ()
Ответ на: комментарий от Demolitionman

а почему ты для дублицирования не используешь средства встроенные в БД ? ( кластеризация ). Зачем велосипеды-то городить ?

+ непонятно как ты собираешся синхронизировать БД + определять какая из БД недоступна

Jopich1 ()
Ответ на: комментарий от x3al

Если я правильно понял, запись производим в одну БД, а читаем с обеих? данная запись

def allow_syncdb(self, db, model):
        "Make sure only the default db allows syncdb"
        return db == 'default'
по сути выполняет python manage.py migrate

А что если производить синхронизацию баз данных на серверах postgre? Кстати еще не думал каким софтом это лучше будет реализовать.

Demolitionman ()
Ответ на: комментарий от Jopich1

Кластеризация? Потому что не знал про этот инструмент) Сейчас почитаю. На сколько я понимаю, ip BD будет один а базы две?

Demolitionman ()
Ответ на: комментарий от x3al

Ваш скрипт из коробки не заработал, по чему то не перенаправлял на др базу при падении какой либо. Сделал так: python.py

DATABASES = {
    'default': {},
    'master': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': '',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': 5432,
    },
    'slave': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': '',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': 5432,
    }
}
SLAVE_DATABASES = ['slave']

DATABASE_ROUTERS = ['db_route.MasterSlaveRouter']
db_route.py
from django.conf import settings
import socket


def test_connection_to_db(database_name):
    try:
        db_definition = getattr(settings, 'DATABASES')[database_name]
        s = socket.create_connection(
            (db_definition['HOST'], db_definition['PORT']), 5)
        s.close()
        return True
    except:
        return False


class MasterSlaveRouter(object):
    def db_for_read(self, model, **hints):
        """
        Reads go to a randomly-chosen slave.
        """
        if test_connection_to_db('master'):
            return 'master'
        return 'slave'

    def db_for_write(self, model, **hints):
        """
        Writes always go to master.
        """
        if test_connection_to_db('master'):
            return 'master'
        return 'slave'

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

    def allow_migrate(self, db, model):
        """
        All non-auth models end up in this pool.
        """
        return True
После перезагрузки gunicorn, не могу авторизоваться в админке, логин и пароль не подходят. создавал нового superuser, делал migrate, все равно не прокатило. Но за то работает перенаправление если какая то из баз лежит.

Если я правильно понимаю то данный роут позволяет производить запись и в бд master и в slave, что как вы писали не есть хорошо. И еще, объясните пожалуйста, данный скрипт будет реплицировать БД или это делать на стороне postgre к примеру pg_pool2?

Demolitionman ()
Ответ на: комментарий от Jopich1

С определением какая из БД недоступна справился средствами [DATABASE ROUTE] Почитал на хабре (кому интересно Тут) про отказоустойчивый кластер и чес сказать был немного удивлен, целая простыня страниц на 10. куча доп софта, скрипты, ssh тунели и все это для казалось бы тривиальной задачи, которая, должна по умолчанию входить в состав продукта (Видимо это относится только к платным БД). Так же почитал инфу на темы Slony-I и pgpool-II. Если не настрою репликацию с помощью django буду использовать Slony. Так же натыкался на коменты по поводу того что лучше это все делать на стороне приложения (использование 2х бд, синхронизация данных).

Demolitionman ()
Ответ на: комментарий от Demolitionman

Не дай Бог после тебя эту «кашу» кому-то разгребать.

commit ()
Ответ на: комментарий от commit

Ну кашу я во первых еще не сготовил. Во вторых это мой первый проект и в третьих эту кашу буду разгребать только я. А вы вместо того что бы повышать свою самооценку благодаря мне, лучше бы чего посоветовали.

Demolitionman ()
Ответ на: комментарий от commit

очередной мой релиз данного проекта уже немного по лучше. По совету жопыча я настроил таки репликацию на серверах postgre. Начиная с 9й версии это можно делать при помощи streaming replication. Теперь имею связку Master/Slave. Django будет писать только в Master, а считывать с обоих серверов. Надеюсь такую «кашу» вполне себе можно админить?

Demolitionman ()
Ответ на: комментарий от Demolitionman

Такая конфигурация вполне подойдет, но для распределения нагрузки между базами. Проблема отказоустойчивости еще не решена.

gruy ★★★ ()
Ответ на: комментарий от gruy

Есть такое. Сейчас мне что бы переназначить упавшего master, придется ручками добавлять тригер-файл на slave. Делал тесты данного деяния, действительно slave даже без ребута превращается в master, после добавления тригер-файла. Но опять же возвращаюсь к Database_route в Django. Там нужно доп проверку делать, если master не доступен, пробовать записать в slave, а у меня с этими скриптами проблема + еще устаревшие функции. То что выше писал на счет роутов, оказалось в итоге совершенно не рабочее. По этому пока что в поисках.

Demolitionman ()
Ответ на: комментарий от gruy

Подскажи пж по поводу синхронизации 2х серверов Django. По моей схеме env директория на обоих серверах должна быть идентичная. Есть ли у Django какой то свой механизм для синхронизации или тупо использовать csync2 как на серверах nginx?

Demolitionman ()
Ответ на: комментарий от Demolitionman

Обычная практика - рабочее окружение через virtualenv и pip, список внешних библиотек проекта указывают в requirements.txt.

Для деплоя есть Fabric, Ansible могут использовать. Решений много.

gruy ★★★ ()
Ответ на: комментарий от gruy

рабочее окружение через virtualenv и pip, список внешних >библиотек проекта указывают в requirements.txt

С этим разобрался. с Fabric сложнее. Можно подробней объяснить как оно работает? Нужно ли его устанавливать на обоих серверах django? Как разрабы на виндовых машинах будут писать-деплоить проекты? Вообще, переговорить бы от и до по поводу этой системы, тяжело поднимать сервер не зная внутренних механизмов его работы.

Demolitionman ()
Ответ на: комментарий от Demolitionman

Вот тебе пример fabfile'а для джанго. Предполагается, что исходники лежат в каком-то удаленном mercurial, например, на bitbucket. Запускается с машины разработчика. На сервера дополнительно ничего ставить не надо.

from fabric.api import env, cd, prefix, local, run, sudo, shell_env, roles
from fabric.operations import get

env.roledefs = {
    'srv1': ['user@srv1.ru'],
}

@roles('srv1')
def deploy():
    PROJECT_DIR = '~/project1/prj'
    VIRTUALENV_SHELL = "source ~/.virtualenvs/project1/bin/activate"
    SETTINGS_FILE = 'config.settings.settings_production'

    with cd(PROJECT_DIR):
        run("hg pull -u")
        with prefix(VIRTUALENV_SHELL):
            run('pip install -r ../requirements.txt')
            with shell_env(DJANGO_SETTINGS_MODULE=SETTINGS_FILE):
                run('python manage.py syncdb')
                run('python manage.py migrate')
                run('python manage.py collectstatic --noinput')
    run('touch ~/project1.socket')
pawnhearts ★★★★★ ()
Последнее исправление: pawnhearts (всего исправлений: 2)
Ответ на: комментарий от gruy

Добрый вечер!Тут кое какие изменения в проекте. Изменился фрэймворк с Django на Flask. И если у меня были трудности с реализацией 2х бд в Django, то с Flask я даже инфы не нашел, каким образом читать с 2х БД pgsql. Это вообще реализуемо? Или нужно использовать 1 балансировщик запросов к БД?

Demolitionman ()
Ответ на: комментарий от Demolitionman

В твоем случае лучше настроить балансировщик для БД и использовать в программе один его адрес.

gruy ★★★ ()
Ответ на: комментарий от gruy

Тоже думаю про балансировщик.pgpool-2 к примеру. Но есть один минус, это ведь единая точка отказа. А по ТЗ такой точки не должно быть.

Demolitionman ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.