Skip to content

Commit 14f52b5

Browse files
authored
Merge pull request #109 from tarantool/kbelyavs/gh-106
Implement reconnection strategy class
2 parents cbb09e0 + 436218d commit 14f52b5

24 files changed

+243
-7
lines changed

Diff for: .gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "test-run"]
2+
path = test-run
3+
url = https://github.com/tarantool/test-run

Diff for: Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
.PHONY: test
12
test:
2-
python -m pytest
3+
python setup.py test
4+
cd test && ./test-run.py
35
coverage:
46
python -m coverage run -p --source=. setup.py test
57
cov-html:

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
# Test runner
4040
# python setup.py test
4141
try:
42-
from tests.setup_command import test
42+
from unit.setup_command import test
4343
cmdclass["test"] = test
4444
except ImportError:
4545
pass

Diff for: tarantool/__init__.py

+26-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# pylint: disable=C0301,W0105,W0401,W0614
33

44
from tarantool.connection import Connection
5+
from tarantool.mesh_connection import MeshConnection
56
from tarantool.const import (
67
SOCKET_TIMEOUT,
78
RECONNECT_MAX_ATTEMPTS,
@@ -50,5 +51,28 @@ def connect(host="localhost", port=33013, user=None, password=None,
5051
encoding=encoding)
5152

5253

53-
__all__ = ['connect', 'Connection', 'Schema', 'Error', 'DatabaseError',
54-
'NetworkError', 'NetworkWarning', 'SchemaError']
54+
def connectmesh(addrs=({'host': 'localhost', 'port': 3301},), user=None,
55+
password=None, encoding=ENCODING_DEFAULT):
56+
'''
57+
Create a connection to the mesh of Tarantool servers.
58+
59+
:param list addrs: A list of maps: {'host':(HOSTNAME|IP_ADDR), 'port':PORT}.
60+
61+
:rtype: :class:`~tarantool.mesh_connection.MeshConnection`
62+
63+
:raise: `NetworkError`
64+
'''
65+
66+
return MeshConnection(addrs=addrs,
67+
user=user,
68+
password=password,
69+
socket_timeout=SOCKET_TIMEOUT,
70+
reconnect_max_attempts=RECONNECT_MAX_ATTEMPTS,
71+
reconnect_delay=RECONNECT_DELAY,
72+
connect_now=True,
73+
encoding=encoding)
74+
75+
76+
__all__ = ['connect', 'Connection', 'connectmesh', 'MeshConnection', 'Schema',
77+
'Error', 'DatabaseError', 'NetworkError', 'NetworkWarning',
78+
'SchemaError']

Diff for: tarantool/mesh_connection.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# -*- coding: utf-8 -*-
2+
'''
3+
This module provides MeshConnection class with automatic switch
4+
between tarantool instances and basic Round-Robin strategy.
5+
'''
6+
7+
from tarantool.connection import Connection
8+
from tarantool.error import NetworkError
9+
from tarantool.utils import ENCODING_DEFAULT
10+
from tarantool.const import (
11+
SOCKET_TIMEOUT,
12+
RECONNECT_MAX_ATTEMPTS,
13+
RECONNECT_DELAY
14+
)
15+
16+
17+
class RoundRobinStrategy(object):
18+
def __init__(self, addrs):
19+
self.addrs = addrs
20+
self.pos = 0
21+
22+
def getnext(self):
23+
tmp = self.pos
24+
self.pos = (self.pos + 1) % len(self.addrs)
25+
return self.addrs[tmp]
26+
27+
28+
class MeshConnection(Connection):
29+
def __init__(self, addrs,
30+
user=None,
31+
password=None,
32+
socket_timeout=SOCKET_TIMEOUT,
33+
reconnect_max_attempts=RECONNECT_MAX_ATTEMPTS,
34+
reconnect_delay=RECONNECT_DELAY,
35+
connect_now=True,
36+
encoding=ENCODING_DEFAULT,
37+
strategy_class=RoundRobinStrategy):
38+
self.nattempts = 2 * len(addrs) + 1
39+
self.strategy = strategy_class(addrs)
40+
addr = self.strategy.getnext()
41+
host = addr['host']
42+
port = addr['port']
43+
super(MeshConnection, self).__init__(host=host,
44+
port=port,
45+
user=user,
46+
password=password,
47+
socket_timeout=socket_timeout,
48+
reconnect_max_attempts=reconnect_max_attempts,
49+
reconnect_delay=reconnect_delay,
50+
connect_now=connect_now,
51+
encoding=encoding)
52+
53+
def _opt_reconnect(self):
54+
nattempts = self.nattempts
55+
while nattempts > 0:
56+
try:
57+
super(MeshConnection, self)._opt_reconnect()
58+
break
59+
except NetworkError:
60+
nattempts -= 1
61+
addr = self.strategy.getnext()
62+
self.host = addr['host']
63+
self.port = addr['port']
64+
else:
65+
raise NetworkError

Diff for: test-run

Submodule test-run added at b85d7ed

Diff for: test/.tarantoolctl

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Options for test-run tarantoolctl
2+
3+
local workdir = os.getenv('TEST_WORKDIR')
4+
default_cfg = {
5+
pid_file = workdir,
6+
wal_dir = workdir,
7+
memtx_dir = workdir,
8+
vinyl_dir = workdir,
9+
log = workdir,
10+
background = false,
11+
}
12+
13+
instance_dir = workdir
14+
15+
-- vim: set ft=lua :

Diff for: test/cluster-py/instance.lua

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env tarantool
2+
3+
local INSTANCE_ID = string.match(arg[0], "%d")
4+
local SOCKET_DIR = require('fio').cwd()
5+
6+
local function instance_uri(instance_id)
7+
return SOCKET_DIR..'/instance'..instance_id..'.sock';
8+
end
9+
10+
require('console').listen(os.getenv('ADMIN'))
11+
12+
box.cfg({
13+
--listen = os.getenv("LISTEN"),
14+
listen = instance_uri(INSTANCE_ID),
15+
memtx_memory = 107374182,
16+
})

Diff for: test/cluster-py/instance1.lua

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
instance.lua

Diff for: test/cluster-py/instance2.lua

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
instance.lua

Diff for: test/cluster-py/master.lua

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env tarantool
2+
os = require('os')
3+
box.cfg({
4+
listen = os.getenv("LISTEN"),
5+
memtx_memory = 107374182,
6+
replication_timeout = 0.1
7+
})
8+
9+
require('console').listen(os.getenv('ADMIN'))

Diff for: test/cluster-py/multi.result

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
box.schema.user.grant('guest', 'read,write,execute', 'universe')
2+
---
3+
...
4+
_ = box.schema.space.create('test')
5+
---
6+
...
7+
_ = box.space.test:create_index('primary')
8+
---
9+
...
10+
box.schema.user.grant('guest', 'read,write,execute', 'universe')
11+
---
12+
...
13+
_ = box.schema.space.create('test')
14+
---
15+
...
16+
_ = box.space.test:create_index('primary')
17+
---
18+
...
19+
- [1, 0]
20+
- [1, 1]
21+
- [1, 0]
22+
NetworkError !

Diff for: test/cluster-py/multi.test.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import sys
2+
import os
3+
import time
4+
import yaml
5+
from lib.tarantool_server import TarantoolServer
6+
sys.path.append('../tarantool')
7+
from mesh_connection import MeshConnection
8+
from tarantool.const import (
9+
SOCKET_TIMEOUT,
10+
RECONNECT_DELAY,
11+
)
12+
from tarantool.error import NetworkError
13+
from tarantool.utils import ENCODING_DEFAULT
14+
15+
INSTANCE_N = 2
16+
17+
18+
def check_connection(con):
19+
try:
20+
s = con.space('test')
21+
print s.select()
22+
except NetworkError:
23+
print 'NetworkError !'
24+
except Exception as e:
25+
print e
26+
27+
28+
# Start instances
29+
master = server
30+
cluster = [master]
31+
for i in range(INSTANCE_N):
32+
server = TarantoolServer(server.ini)
33+
server.script = 'cluster-py/instance%d.lua' % (i+1)
34+
server.vardir = os.path.join(server.vardir, 'instance', str(i))
35+
server.deploy()
36+
server.admin("box.schema.user.grant('guest', 'read,write,execute', 'universe')")
37+
server.admin("_ = box.schema.space.create('test')")
38+
server.admin("_ = box.space.test:create_index('primary')")
39+
server.admin("box.space.test:insert{%d, %s}" % (1, i), silent = True)
40+
cluster.append(server)
41+
42+
# Make a list of servers
43+
sources = []
44+
for server in cluster[1:]:
45+
sources.append(yaml.load(server.admin('box.cfg.listen', silent=True))[0])
46+
47+
addrs = []
48+
for addr in sources:
49+
addrs.append({'host': None, 'port': addr})
50+
51+
con = MeshConnection(addrs=addrs,
52+
user=None,
53+
password=None,
54+
socket_timeout=SOCKET_TIMEOUT,
55+
reconnect_max_attempts=0,
56+
reconnect_delay=RECONNECT_DELAY,
57+
connect_now=True,
58+
encoding=ENCODING_DEFAULT)
59+
60+
cluster[0].stop() # stop server - no effect
61+
check_connection(con) # instance#1
62+
cluster[1].stop() # stop instance#1
63+
check_connection(con) # instance#2
64+
cluster[1].start() # start instance#1
65+
cluster[2].stop() # stop instance#2
66+
check_connection(con) # instance#1 again
67+
cluster[1].stop() # stop instance#1
68+
check_connection(con) # both stopped: NetworkError !
69+
70+
master.cleanup()
71+
master.deploy()

Diff for: test/cluster-py/suite.ini

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[default]
2+
core = tarantool
3+
script = master.lua
4+
description = reconnect
5+
is_parallel = False

Diff for: test/test-run.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../test-run/test-run.py

Diff for: tests/__init__.py renamed to unit/__init__.py

File renamed without changes.

Diff for: tests/setup_command.py renamed to unit/setup_command.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ def run(self):
2222
Find all tests in test/tarantool/ and run them
2323
'''
2424

25-
tests = unittest.defaultTestLoader.discover('tests')
25+
tests = unittest.defaultTestLoader.discover('unit')
2626
test_runner = unittest.TextTestRunner(verbosity = 2)
2727
test_runner.run(tests)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Diff for: tests/suites/test_dml.py renamed to unit/suites/test_dml.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def setUpClass(self):
1111
print(' DML '.center(70, '='))
1212
print('-' * 70)
1313
self.srv = TarantoolServer()
14-
self.srv.script = 'tests/suites/box.lua'
14+
self.srv.script = 'unit/suites/box.lua'
1515
self.srv.start()
1616
self.con = tarantool.Connection('localhost', self.srv.args['primary'])
1717
self.adm = self.srv.admin
File renamed without changes.

Diff for: tests/suites/test_schema.py renamed to unit/suites/test_schema.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def setUpClass(self):
1010
print(' SCHEMA '.center(70, '='))
1111
print('-' * 70)
1212
self.srv = TarantoolServer()
13-
self.srv.script = 'tests/suites/box.lua'
13+
self.srv.script = 'unit/suites/box.lua'
1414
self.srv.start()
1515
self.con = tarantool.Connection('localhost', self.srv.args['primary'])
1616
self.sch = self.con.schema

0 commit comments

Comments
 (0)