diff --git a/arango/database.py b/arango/database.py index 7348c25..22f6d73 100644 --- a/arango/database.py +++ b/arango/database.py @@ -52,6 +52,7 @@ ServerLicenseGetError, ServerLicenseSetError, ServerLogLevelError, + ServerLogLevelResetError, ServerLogLevelSetError, ServerLogSettingError, ServerLogSettingSetError, @@ -1003,6 +1004,35 @@ def response_handler(resp: Response) -> Json: return self._execute(request, response_handler) + def reset_log_levels(self, server_id: Optional[str] = None) -> Result[Json]: + """Reset the logging levels. + + Revert the server’s log level settings to the values they had at startup, + as determined by the startup options specified on the command-line, + a configuration file, and the factory defaults. + + :param server_id: Forward log level to a specific server. This makes it + easier to adjust the log levels in clusters because DB-Servers require + JWT authentication whereas Coordinators also support authentication + using usernames and passwords. + :type server_id: str | None + :return: New logging levels. + :rtype: dict + """ + params: Params = {} + if server_id is not None: + params["serverId"] = server_id + + request = Request(method="delete", endpoint="/_admin/log/level", params=params) + + def response_handler(resp: Response) -> Json: + if not resp.is_success: + raise ServerLogLevelResetError(resp, request) + result: Json = resp.body + return result + + return self._execute(request, response_handler) + def reload_routing(self) -> Result[bool]: """Reload the routing information. @@ -3020,6 +3050,7 @@ def begin_transaction( allow_implicit: Optional[bool] = None, lock_timeout: Optional[int] = None, max_size: Optional[int] = None, + skip_fast_lock_round: Optional[bool] = None, ) -> "TransactionDatabase": """Begin a transaction. @@ -3043,6 +3074,9 @@ def begin_transaction( :type lock_timeout: int | None :param max_size: Max transaction size in bytes. :type max_size: int | None + :param skip_fast_lock_round: Whether to disable fast locking for write + operations. + :type skip_fast_lock_round: bool | None :return: Database API wrapper object specifically for transactions. :rtype: arango.database.TransactionDatabase """ @@ -3055,6 +3089,7 @@ def begin_transaction( allow_implicit=allow_implicit, lock_timeout=lock_timeout, max_size=max_size, + skip_fast_lock_round=skip_fast_lock_round, ) def begin_controlled_execution( @@ -3191,6 +3226,8 @@ class TransactionDatabase(Database): :param transaction_id: Initialize using an existing transaction instead of creating a new transaction. :type transaction_id: str | None + :param skip_fast_lock_round: Whether to disable fast locking for write operations. + :type skip_fast_lock_round: bool | None """ def __init__( @@ -3204,6 +3241,7 @@ def __init__( lock_timeout: Optional[int] = None, max_size: Optional[int] = None, transaction_id: Optional[str] = None, + skip_fast_lock_round: Optional[bool] = None, ) -> None: self._executor: TransactionApiExecutor super().__init__( @@ -3218,6 +3256,7 @@ def __init__( lock_timeout=lock_timeout, max_size=max_size, transaction_id=transaction_id, + skip_fast_lock_round=skip_fast_lock_round, ), ) diff --git a/arango/exceptions.py b/arango/exceptions.py index d03fd84..58d9908 100644 --- a/arango/exceptions.py +++ b/arango/exceptions.py @@ -674,6 +674,10 @@ class ServerLogLevelError(ArangoServerError): """Failed to retrieve server log levels.""" +class ServerLogLevelResetError(ArangoServerError): + """Failed to reset server log levels.""" + + class ServerLogSettingError(ArangoServerError): """Failed to retrieve server log settings.""" diff --git a/arango/executor.py b/arango/executor.py index b854c67..9330e11 100644 --- a/arango/executor.py +++ b/arango/executor.py @@ -245,6 +245,8 @@ class TransactionApiExecutor: :param transaction_id: Initialize using an existing transaction instead of starting a new transaction. :type transaction_id: str | None + :param skip_fast_lock_round: Whether to disable fast locking for write operations. + :type skip_fast_lock_round: bool | None """ def __init__( @@ -259,6 +261,7 @@ def __init__( max_size: Optional[int] = None, allow_dirty_read: bool = False, transaction_id: Optional[str] = None, + skip_fast_lock_round: Optional[bool] = None, ) -> None: self._conn = connection @@ -279,6 +282,8 @@ def __init__( data["lockTimeout"] = lock_timeout if max_size is not None: data["maxTransactionSize"] = max_size + if skip_fast_lock_round is not None: + data["skipFastLockRound"] = skip_fast_lock_round if transaction_id is None: request = Request( diff --git a/pyproject.toml b/pyproject.toml index 9f792f0..d3930e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,6 @@ dev = [ "pytest-cov>=3.0.0", "sphinx", "sphinx_rtd_theme", - "types-pkg_resources", "types-requests", "types-setuptools", ] diff --git a/starter.sh b/starter.sh index be1778a..b4e39f2 100755 --- a/starter.sh +++ b/starter.sh @@ -6,7 +6,7 @@ # Usage: # ./starter.sh [single|cluster] [community|enterprise] [version] # Example: -# ./starter.sh cluster enterprise 3.11.4 +# ./starter.sh cluster enterprise 3.12.1 setup="${1:-single}" license="${2:-community}" diff --git a/tests/test_database.py b/tests/test_database.py index cab4bd2..6ac64e1 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -24,6 +24,7 @@ ServerEngineError, ServerLicenseSetError, ServerLogLevelError, + ServerLogLevelResetError, ServerLogLevelSetError, ServerMetricsError, ServerModeSetError, @@ -65,7 +66,7 @@ def test_database_attributes(db, username): assert isinstance(db.wal, WAL) -def test_database_misc_methods(client, sys_db, db, bad_db, cluster, secret): +def test_database_misc_methods(client, sys_db, db, bad_db, cluster, secret, db_version): # Test get properties properties = db.properties() assert "id" in properties @@ -249,7 +250,8 @@ def test_database_misc_methods(client, sys_db, db, bad_db, cluster, secret): assert err.value.error_code in {11, 1228} # Test get log levels - assert isinstance(sys_db.log_levels(), dict) + default_log_levels = sys_db.log_levels() + assert isinstance(default_log_levels, dict) # Test get log levels with bad database with assert_raises(ServerLogLevelError) as err: @@ -296,6 +298,17 @@ def test_database_misc_methods(client, sys_db, db, bad_db, cluster, secret): assert "username" not in result_1 assert result_1 == result_2 + # Reset Log Settings + if db.version() >= "3.12.1": + if cluster: + server_id = sys_db.cluster.server_id() + assert isinstance(sys_db.reset_log_levels(server_id), dict) + + result = sys_db.reset_log_levels() + assert result == default_log_levels + with assert_raises(ServerLogLevelResetError): + bad_db.reset_log_levels() + # Test get storage engine engine = db.engine() assert engine["name"] in ["rocksdb"] diff --git a/tests/test_transaction.py b/tests/test_transaction.py index dedf54e..75ec28a 100644 --- a/tests/test_transaction.py +++ b/tests/test_transaction.py @@ -1,4 +1,5 @@ import pytest +from packaging import version from arango.database import TransactionDatabase from arango.exceptions import ( @@ -16,14 +17,15 @@ def test_transaction_execute_raw(db, col, docs): # Test execute raw transaction doc = docs[0] key = doc["_key"] - result = db.execute_transaction( - command=f""" + command = f""" function (params) {{ var db = require('internal').db; db.{col.name}.save({{'_key': params.key, 'val': 1}}); return true; }} - """, + """ # noqa: E702 E231 E272 E202 + result = db.execute_transaction( + command=command, params={"key": key}, write=[col.name], read=[col.name], @@ -43,7 +45,7 @@ def test_transaction_execute_raw(db, col, docs): assert err.value.error_code == 10 -def test_transaction_init(db, bad_db, col, username): +def test_transaction_init(db, db_version, bad_db, col, username): txn_db = db.begin_transaction() assert isinstance(txn_db, TransactionDatabase) @@ -68,6 +70,22 @@ def test_transaction_init(db, bad_db, col, username): bad_db.begin_transaction() assert err.value.error_code in {11, 1228} + # Test all options + kwargs = dict( + read=col.name, + write=col.name, + exclusive=[], + sync=True, + allow_implicit=False, + lock_timeout=1000, + max_size=1024 * 1024, + ) + if db_version >= version.parse("3.12.1"): + kwargs["skip_fast_lock_round"] = True + txn_db = db.begin_transaction(**kwargs) + assert isinstance(txn_db, TransactionDatabase) + assert txn_db.transaction_id is not None + def test_transaction_status(db, col, docs): txn_db = db.begin_transaction(read=col.name)