Skip to content

Commit 0972c54

Browse files
committed
api: extend connect with fetch_schema param
Added support of the fetch_schema parameter, which allows to ignore schema changes on the server. Closes #219
1 parent f7e27a5 commit 0972c54

10 files changed

+310
-33
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,5 @@ test/data/*.key
5353
!test/data/localhost.enc.key
5454
test/data/*.pem
5555
test/data/*.srl
56+
57+
.rocks

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
to decrypt private SSL key file (#224).
1414
- Support specifying authentication method with `auth_type`
1515
and Tarantool EE `pap-sha256` authentication method (#269).
16+
- Support of the fetch_schema parameter for connection (#219).
1617

1718
### Changed
1819

docs/source/quick-start.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ Through the :class:`~tarantool.Connection` object, you can access
359359
360360
>>> import tarantool
361361
>>> from tarantool.error import CrudModuleError, CrudModuleManyError, DatabaseError
362-
>>> conn = tarantool.Connection(host='localhost',port=3301)
362+
>>> conn = tarantool.Connection(host='localhost',port=3301,fetch_schema=False)
363363
364364
>>> conn.crud_
365365
conn.crud_count( conn.crud_insert( conn.crud_insert_object_many(

tarantool/connection.py

+65-11
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,8 @@ def __init__(self, host, port,
579579
ssl_password_file=DEFAULT_SSL_PASSWORD_FILE,
580580
packer_factory=default_packer_factory,
581581
unpacker_factory=default_unpacker_factory,
582-
auth_type=None):
582+
auth_type=None,
583+
fetch_schema=True):
583584
"""
584585
:param host: Server hostname or IP address. Use ``None`` for
585586
Unix sockets.
@@ -736,6 +737,21 @@ def __init__(self, host, port,
736737
``"chap-sha1"``.
737738
:type auth_type: :obj:`None` or :obj:`str`, optional
738739
740+
:param bool fetch_schema: If ``False``, allows to ignore schema
741+
changes on the server, schema updates are not automatically
742+
loaded. As a result, these methods become unavailable:
743+
:meth:`~tarantool.Connection.replace`,
744+
:meth:`~tarantool.Connection.insert`,
745+
:meth:`~tarantool.Connection.delete`,
746+
:meth:`~tarantool.Connection.upsert`,
747+
:meth:`~tarantool.Connection.update`,
748+
:meth:`~tarantool.Connection.select`,
749+
:meth:`~tarantool.Connection.space`.
750+
Calling the :meth:`~tarantool.Connection.update_schema`
751+
method causes this parameter to switch
752+
from ``False`` to ``True``.
753+
:type fetch_schema: :obj:`bool`, optional
754+
739755
:raise: :exc:`~tarantool.error.ConfigurationError`,
740756
:meth:`~tarantool.Connection.connect` exceptions
741757
@@ -766,8 +782,9 @@ def __init__(self, host, port,
766782
self.socket_timeout = socket_timeout
767783
self.reconnect_delay = reconnect_delay
768784
self.reconnect_max_attempts = reconnect_max_attempts
785+
self.fetch_schema = fetch_schema
769786
self.schema = Schema(self)
770-
self.schema_version = 1
787+
self.schema_version = 0
771788
self._socket = None
772789
self.connected = False
773790
self.error = True
@@ -1023,7 +1040,8 @@ def connect(self):
10231040
if self.transport == SSL_TRANSPORT:
10241041
self.wrap_socket_ssl()
10251042
self.handshake()
1026-
self.load_schema()
1043+
if self.schema.schema != {}:
1044+
self.load_schema()
10271045
except SslError as e:
10281046
raise e
10291047
except Exception as e:
@@ -1118,7 +1136,8 @@ def _send_request_wo_reconnect(self, request, on_push=None, on_push_ctx=None):
11181136
response = request.response_class(self, self._read_response())
11191137
break
11201138
except SchemaReloadException as e:
1121-
self.update_schema(e.schema_version)
1139+
if self.schema.schema != {}:
1140+
self.update_schema(e.schema_version)
11221141
continue
11231142

11241143
while response._code == IPROTO_CHUNK:
@@ -1255,6 +1274,9 @@ def update_schema(self, schema_version):
12551274
:meta private:
12561275
"""
12571276

1277+
if self.fetch_schema == False:
1278+
self.fetch_schema = True
1279+
12581280
self.schema_version = schema_version
12591281
self.flush_schema()
12601282

@@ -1269,6 +1291,18 @@ def flush_schema(self):
12691291
self.schema.flush()
12701292
self.load_schema()
12711293

1294+
def _fetch_schema_support_check(self):
1295+
"""
1296+
Checks the fetch_schema opt of the connection is enabled.
1297+
If the opt is disabled, an exception will be thrown
1298+
about unsupporting the method with fetch_schema=False.
1299+
1300+
:raise: :exc:`~tarantool.error.NotSupportedError`
1301+
"""
1302+
if not self.fetch_schema:
1303+
raise NotSupportedError('This method is not available in ' +
1304+
'connection opened with fetch_schema=False')
1305+
12721306
def call(self, func_name, *args, on_push=None, on_push_ctx=None):
12731307
"""
12741308
Execute a CALL request: call a stored Lua function.
@@ -1366,11 +1400,14 @@ def replace(self, space_name, values, on_push=None, on_push_ctx=None):
13661400
:exc:`~tarantool.error.DatabaseError`,
13671401
:exc:`~tarantool.error.SchemaError`,
13681402
:exc:`~tarantool.error.NetworkError`,
1369-
:exc:`~tarantool.error.SslError`
1403+
:exc:`~tarantool.error.SslError`,
1404+
:exc:`~tarantool.error.NotSupportedError`
13701405
13711406
.. _replace: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/replace/
13721407
"""
13731408

1409+
self._fetch_schema_support_check()
1410+
13741411
if isinstance(space_name, str):
13751412
space_name = self.schema.get_space(space_name).sid
13761413
if on_push is not None and not callable(on_push):
@@ -1411,7 +1448,7 @@ def authenticate(self, user, password):
14111448
password=self.password,
14121449
auth_type=self._get_auth_type())
14131450
auth_response = self._send_request_wo_reconnect(request)
1414-
if auth_response.return_code == 0:
1451+
if auth_response.return_code == 0 and self.fetch_schema:
14151452
self.flush_schema()
14161453
return auth_response
14171454

@@ -1584,11 +1621,14 @@ def insert(self, space_name, values, on_push=None, on_push_ctx=None):
15841621
:exc:`~tarantool.error.DatabaseError`,
15851622
:exc:`~tarantool.error.SchemaError`,
15861623
:exc:`~tarantool.error.NetworkError`,
1587-
:exc:`~tarantool.error.SslError`
1624+
:exc:`~tarantool.error.SslError`,
1625+
:exc:`~tarantool.error.NotSupportedError`
15881626
15891627
.. _insert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/insert/
15901628
"""
15911629

1630+
self._fetch_schema_support_check()
1631+
15921632
if isinstance(space_name, str):
15931633
space_name = self.schema.get_space(space_name).sid
15941634
if on_push is not None and not callable(on_push):
@@ -1623,11 +1663,14 @@ def delete(self, space_name, key, *, index=0, on_push=None, on_push_ctx=None):
16231663
:exc:`~tarantool.error.DatabaseError`,
16241664
:exc:`~tarantool.error.SchemaError`,
16251665
:exc:`~tarantool.error.NetworkError`,
1626-
:exc:`~tarantool.error.SslError`
1666+
:exc:`~tarantool.error.SslError`,
1667+
:exc:`~tarantool.error.NotSupportedError`
16271668
16281669
.. _delete: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/delete/
16291670
"""
16301671

1672+
self._fetch_schema_support_check()
1673+
16311674
key = wrap_key(key)
16321675
if isinstance(space_name, str):
16331676
space_name = self.schema.get_space(space_name).sid
@@ -1682,11 +1725,14 @@ def upsert(self, space_name, tuple_value, op_list, *, index=0, on_push=None, on_
16821725
:exc:`~tarantool.error.DatabaseError`,
16831726
:exc:`~tarantool.error.SchemaError`,
16841727
:exc:`~tarantool.error.NetworkError`,
1685-
:exc:`~tarantool.error.SslError`
1728+
:exc:`~tarantool.error.SslError`,
1729+
:exc:`~tarantool.error.NotSupportedError`
16861730
16871731
.. _upsert: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/upsert/
16881732
"""
16891733

1734+
self._fetch_schema_support_check()
1735+
16901736
if isinstance(space_name, str):
16911737
space_name = self.schema.get_space(space_name).sid
16921738
if isinstance(index, str):
@@ -1770,11 +1816,14 @@ def update(self, space_name, key, op_list, *, index=0, on_push=None, on_push_ctx
17701816
:exc:`~tarantool.error.DatabaseError`,
17711817
:exc:`~tarantool.error.SchemaError`,
17721818
:exc:`~tarantool.error.NetworkError`,
1773-
:exc:`~tarantool.error.SslError`
1819+
:exc:`~tarantool.error.SslError`,
1820+
:exc:`~tarantool.error.NotSupportedError`
17741821
17751822
.. _update: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/update/
17761823
"""
17771824

1825+
self._fetch_schema_support_check()
1826+
17781827
key = wrap_key(key)
17791828
if isinstance(space_name, str):
17801829
space_name = self.schema.get_space(space_name).sid
@@ -1956,11 +2005,14 @@ def select(self, space_name, key=None, *, offset=0, limit=0xffffffff, index=0, i
19562005
:exc:`~tarantool.error.DatabaseError`,
19572006
:exc:`~tarantool.error.SchemaError`,
19582007
:exc:`~tarantool.error.NetworkError`,
1959-
:exc:`~tarantool.error.SslError`
2008+
:exc:`~tarantool.error.SslError`,
2009+
:exc:`~tarantool.error.NotSupportedError`
19602010
19612011
.. _select: https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/select/
19622012
"""
19632013

2014+
self._fetch_schema_support_check()
2015+
19642016
if iterator is None:
19652017
iterator = ITERATOR_EQ
19662018
if key is None or (isinstance(key, (list, tuple)) and
@@ -1996,6 +2048,8 @@ def space(self, space_name):
19962048
:raise: :exc:`~tarantool.error.SchemaError`
19972049
"""
19982050

2051+
self._fetch_schema_support_check()
2052+
19992053
return Space(self, space_name)
20002054

20012055
def generate_sync(self):

tarantool/connection_pool.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@ def __init__(self,
378378
call_16=False,
379379
connection_timeout=CONNECTION_TIMEOUT,
380380
strategy_class=RoundRobinStrategy,
381-
refresh_delay=POOL_REFRESH_DELAY):
381+
refresh_delay=POOL_REFRESH_DELAY,
382+
fetch_schema=True):
382383
"""
383384
:param addrs: List of dictionaries describing server addresses:
384385
@@ -452,6 +453,9 @@ def __init__(self,
452453
`box.info.ro`_ status background refreshes, in seconds.
453454
:type connection_timeout: :obj:`float`, optional
454455
456+
:param fetch_schema: Refer to
457+
:paramref:`~tarantool.Connection.params.fetch_schema`.
458+
455459
:raise: :exc:`~tarantool.error.ConfigurationError`,
456460
:class:`~tarantool.Connection` exceptions
457461
@@ -500,7 +504,8 @@ def __init__(self,
500504
ssl_ciphers=addr['ssl_ciphers'],
501505
ssl_password=addr['ssl_password'],
502506
ssl_password_file=addr['ssl_password_file'],
503-
auth_type=addr['auth_type'])
507+
auth_type=addr['auth_type'],
508+
fetch_schema=fetch_schema)
504509
)
505510

506511
if connect_now:

tarantool/mesh_connection.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ def __init__(self, host=None, port=None,
283283
addrs=None,
284284
strategy_class=RoundRobinStrategy,
285285
cluster_discovery_function=None,
286-
cluster_discovery_delay=CLUSTER_DISCOVERY_DELAY):
286+
cluster_discovery_delay=CLUSTER_DISCOVERY_DELAY,
287+
fetch_schema=True):
287288
"""
288289
:param host: Refer to
289290
:paramref:`~tarantool.Connection.params.host`.
@@ -425,6 +426,9 @@ def __init__(self, host=None, port=None,
425426
list refresh.
426427
:type cluster_discovery_delay: :obj:`float`, optional
427428
429+
:param fetch_schema: Refer to
430+
:paramref:`~tarantool.Connection.params.fetch_schema`.
431+
428432
:raises: :exc:`~tarantool.error.ConfigurationError`,
429433
:class:`~tarantool.Connection` exceptions,
430434
:class:`~tarantool.MeshConnection.connect` exceptions
@@ -489,7 +493,8 @@ def __init__(self, host=None, port=None,
489493
ssl_ciphers=addr['ssl_ciphers'],
490494
ssl_password=addr['ssl_password'],
491495
ssl_password_file=addr['ssl_password_file'],
492-
auth_type=addr['auth_type'])
496+
auth_type=addr['auth_type'],
497+
fetch_schema=fetch_schema)
493498

494499
def connect(self):
495500
"""

tarantool/request.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,13 @@ def header(self, length):
188188
"""
189189

190190
self._sync = self.conn.generate_sync()
191-
header = self._dumps({IPROTO_REQUEST_TYPE: self.request_type,
192-
IPROTO_SYNC: self._sync,
193-
IPROTO_SCHEMA_ID: self.conn.schema_version})
191+
header_fields = {
192+
IPROTO_REQUEST_TYPE: self.request_type,
193+
IPROTO_SYNC: self._sync,
194+
}
195+
if self.conn.schema.schema != {}:
196+
header_fields[IPROTO_SCHEMA_ID] = self.conn.schema_version
197+
header = self._dumps(header_fields)
194198

195199
return self._dumps(length + len(header)) + header
196200

test/suites/crud_server.lua

+2-4
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,15 @@ box.schema.user.grant(
7171
)
7272

7373
if crud_imported == false or vshard_imported == false then
74+
-- Set flag for unittest.
75+
_G['ROCKS_IMPORT_FAIL'] = true
7476
local fail_msg = 'The crud/vshard modules are not detected, ' ..
7577
'installation via rocks install is required ' ..
7678
'for CRUD testing purposes. You can use ' ..
7779
'<tarantoolctl rocks install crud> or ' ..
7880
'<tt rocks install crud> to install modules'
7981
-- The print output will be captured in the logs.
8082
print(fail_msg)
81-
-- Because of the implementation of the testing framework,
82-
-- this will cause the crash of the CRUD test and display to user
83-
-- a message about require fail during instance configuration.
84-
box.info.status = fail_msg
8583
else
8684
configure_crud_instance(primary_listen, crud, vshard)
8785
end

test/suites/test_crud.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,20 @@ def setUp(self):
3333
time.sleep(1)
3434
# Open connections to instance.
3535
self.conn = tarantool.Connection(host=self.host, port=self.port,
36-
user='guest', password='')
36+
user='guest', password='', fetch_schema=False)
3737
self.conn_mesh = tarantool.MeshConnection(host=self.host, port=self.port,
38-
user='guest', password='')
38+
user='guest', password='', fetch_schema=False)
3939
self.conn_pool = tarantool.ConnectionPool([{'host':self.host, 'port':self.port}],
40-
user='guest', password='')
40+
user='guest', password='',
41+
fetch_schema=False)
4142
# Time for vshard group configuration.
4243
time.sleep(1)
44+
if self.conn.eval('return ROCKS_IMPORT_FAIL').data[0] == True:
45+
raise unittest.SkipTest('The crud/vshard modules are not detected, ' +
46+
'installation via rocks install is required ' +
47+
'for CRUD testing purposes. You can use ' +
48+
'<tarantoolctl rocks install crud> or ' +
49+
'<tt rocks install crud> to install modules')
4350

4451
crud_test_cases = {
4552
'crud_insert': {

0 commit comments

Comments
 (0)