Add a PostgreSQL driver

Change-Id: Id6a6ef4c1ec11c1f8cf5aa440c4f425213837ffc
This commit is contained in:
Julien Danjou 2014-10-08 20:28:11 +02:00
parent b6242610fc
commit b5f87e4fc5
6 changed files with 145 additions and 0 deletions

25
setup-postgresql-env.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash
set -x -e
. functions.sh
function clean_exit() {
local error_code="$?"
${PGSQL_PATH}/pg_ctl -w -D ${PGSQL_DATA} -o "-p $PGSQL_PORT" stop
rm -rf ${PGSQL_DATA}
return $error_code
}
trap "clean_exit" EXIT
# Start PostgreSQL process for tests
PGSQL_DATA=`mktemp -d /tmp/tooz-pgsql-XXXXX`
PGSQL_PATH=`pg_config --bindir`
PGSQL_PORT=9825
${PGSQL_PATH}/initdb ${PGSQL_DATA}
${PGSQL_PATH}/pg_ctl -w -D ${PGSQL_DATA} -o "-k ${PGSQL_DATA} -p ${PGSQL_PORT}" start
# Wait for PostgreSQL to start listening to connections
export TOOZ_TEST_PGSQL_URL="postgresql:///?host=${PGSQL_DATA}&port=${PGSQL_PORT}&dbname=template1"
# Yield execution to venv command
$*

View File

@ -30,6 +30,7 @@ tooz.backends =
memcached = tooz.drivers.memcached:MemcachedDriver
ipc = tooz.drivers.ipc:IPCDriver
redis = tooz.drivers.redis:RedisDriver
postgresql = tooz.drivers.pgsql:PostgresDriver
[build_sphinx]
all_files = 1

View File

@ -9,3 +9,4 @@ testtools>=0.9.32
testscenarios>=0.4
coverage>=3.6
sysv_ipc>=0.6.8
psycopg2

101
tooz/drivers/pgsql.py Normal file
View File

@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
#
# Copyright © 2014 eNovance
#
# Author: Julien Danjou <julien@danjou.info>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import hashlib
import psycopg2
import six
import tooz
from tooz import coordination
from tooz import locking
class PostgresLock(locking.Lock):
"""A PostgreSQL based lock."""
def __init__(self, name, connection):
super(PostgresLock, self).__init__(name)
self._conn = connection
h = hashlib.md5()
h.update(name)
if six.PY2:
self.key = list(map(ord, h.digest()[0:2]))
else:
self.key = h.digest()[0:2]
def acquire(self, blocking=True):
if blocking is not True:
raise tooz.NotImplemented(
"This driver does not support non blocking locks")
with self._conn.cursor() as cur:
cur.execute("SELECT pg_advisory_lock(%s, %s);", self.key)
return True
def release(self):
with self._conn.cursor() as cur:
cur.execute("SELECT pg_advisory_unlock(%s, %s);", self.key)
return cur.fetchone()[0]
class PostgresDriver(coordination.CoordinationDriver):
def __init__(self, member_id, parsed_url, options):
"""Initialize the PostgreSQL driver."""
super(PostgresDriver, self).__init__()
self._host = options.get("host", [None])[-1]
self._port = parsed_url.port or options.get("port", [None])[-1]
self._dbname = parsed_url.path[1:] or options.get("dbname", [None])[-1]
self._username = parsed_url.username
self._password = parsed_url.password
def _start(self):
self._conn = psycopg2.connect(host=self._host,
port=self._port,
user=self._username,
password=self._password,
database=self._dbname)
def _stop(self):
self._conn.close()
def get_lock(self, name):
return PostgresLock(name, self._conn)
@staticmethod
def watch_join_group(group_id, callback):
raise tooz.NotImplemented
@staticmethod
def unwatch_join_group(group_id, callback):
raise tooz.NotImplemented
@staticmethod
def watch_leave_group(group_id, callback):
raise tooz.NotImplemented
@staticmethod
def unwatch_leave_group(group_id, callback):
raise tooz.NotImplemented
@staticmethod
def watch_elected_as_leader(group_id, callback):
raise tooz.NotImplemented
@staticmethod
def unwatch_elected_as_leader(group_id, callback):
raise tooz.NotImplemented

View File

@ -13,6 +13,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import time
import uuid
@ -33,6 +34,7 @@ class TestAPI(testscenarios.TestWithScenarios,
('memcached', {'url': 'memcached://?timeout=5'}),
('ipc', {'url': 'ipc://'}),
('redis', {'url': 'redis://localhost:6379?timeout=5'}),
('postgresql', {'url': os.getenv("TOOZ_TEST_PGSQL_URL")}),
]
# Only certain drivers have the tested support for timeouts that we test
@ -61,6 +63,8 @@ class TestAPI(testscenarios.TestWithScenarios,
def setUp(self):
super(TestAPI, self).setUp()
if self.url is None:
self.skipTest("No URL set for this driver")
self.group_id = self._get_random_uuid()
self.member_id = self._get_random_uuid()
self._coord = tooz.coordination.get_coordinator(self.url,

13
tox.ini
View File

@ -69,6 +69,19 @@ deps = {[testenv:py34]deps}
basepython = python3.4
commands = {toxinidir}/setup-memcached-env.sh python setup.py testr --slowest --testr-args="{posargs}"
[testenv:py27-postgresql]
commands = {toxinidir}/setup-postgresql-env.sh python setup.py testr --slowest --testr-args="{posargs}"
[testenv:py33-postgresql]
deps = {[testenv:py33]deps}
basepython = python3.3
commands = {toxinidir}/setup-postgresql-env.sh python setup.py testr --slowest --testr-args="{posargs}"
[testenv:py34-postgresql]
deps = {[testenv:py34]deps}
basepython = python3.4
commands = {toxinidir}/setup-postgresql-env.sh python setup.py testr --slowest --testr-args="{posargs}"
[testenv:cover]
commands = python setup.py testr --slowest --coverage --testr-args="{posargs}"