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
() автор топика
Ответ на: комментарий от 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

смотри в сторону sqlalchemy. Можешь что и найдешь

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

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

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

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

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