diff --git a/.github/workflows/ubuntu-master.yml b/.github/workflows/ubuntu-master.yml index 2cd8c39c..81518a31 100644 --- a/.github/workflows/ubuntu-master.yml +++ b/.github/workflows/ubuntu-master.yml @@ -10,26 +10,41 @@ on: branches: [ master ] jobs: - build: - + tests-ce: runs-on: ubuntu-latest - + strategy: + matrix: + tarantool-version: [ "1.10", "2.8" ] + fail-fast: false steps: - uses: actions/checkout@v2 + - name: Set up JDK 1.8 uses: actions/setup-java@v1 with: java-version: 1.8 + - name: Cache Maven packages uses: actions/cache@v2 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 + + - name: Cache docker images + uses: satackey/action-docker-layer-caching@v0.0.11 + continue-on-error: true + with: + key: ${{ runner.os }}-docker-layer-cache-${{ matrix.tarantool-version }} + restore-keys: | + ${{ runner.os }}-docker-layer-cache- + - name: Build with Maven run: mvn -B verify --file pom.xml + - name: Run integration tests env: + TARANTOOL_VERSION: ${{ matrix.tarantool-version }} TARANTOOL_SERVER_USER: root TARANTOOL_SERVER_GROUP: root run: mvn -B test -P integration --file pom.xml diff --git a/src/main/java/io/tarantool/driver/exceptions/errors/TarantoolErrors.java b/src/main/java/io/tarantool/driver/exceptions/errors/TarantoolErrors.java index 46c9e298..2d63e4e7 100644 --- a/src/main/java/io/tarantool/driver/exceptions/errors/TarantoolErrors.java +++ b/src/main/java/io/tarantool/driver/exceptions/errors/TarantoolErrors.java @@ -26,20 +26,23 @@ public class TarantoolErrors { private enum ErrorsCodes { NO_CONNECTION("77"), TIMEOUT("78"); private final String code; + ErrorsCodes(String code) { this.code = code; } + public String getCode() { return code; } } + private static final String CLIENT_ERROR = "ClientError"; /** * Produces {@link TarantoolInternalException} subclasses * from the serialized representation in the format of require('errors').new_class("NewError"), - * @see tarantool/errors * + * @see tarantool/errors */ public static class TarantoolErrorsErrorFactory implements TarantoolErrorFactory { private static final StringValue LINE = ValueFactory.newString("line"); @@ -49,12 +52,10 @@ public static class TarantoolErrorsErrorFactory implements TarantoolErrorFactory private static final StringValue ERROR_MESSAGE = ValueFactory.newString("str"); private static final StringValue STACKTRACE = ValueFactory.newString("stack"); private static final Pattern NETWORK_ERROR_PATTERN = Pattern.compile( - ".*" + - "\"code\":" + - "[" + ErrorsCodes.NO_CONNECTION.getCode() + "|" + ErrorsCodes.TIMEOUT.getCode() + "]" + - ".*" + - "\"type\":\"" + CLIENT_ERROR + "\"" + - ".*", Pattern.DOTALL); + "(?=.*\"type\":\"" + CLIENT_ERROR + "\")" + + "(?=.*\"code\":" + + "[" + ErrorsCodes.NO_CONNECTION.getCode() + "|" + ErrorsCodes.TIMEOUT.getCode() + "])", + Pattern.DOTALL); public TarantoolErrorsErrorFactory() { } @@ -67,7 +68,7 @@ public Optional create(Value error) { Map map = error.asMapValue().map(); String exceptionMessage = ""; - String errorMessage = map.containsKey(ERROR_MESSAGE) ? map.get(ERROR_MESSAGE).toString() : null; + String errorMessage = map.containsKey(ERROR_MESSAGE) ? map.get(ERROR_MESSAGE).toString() : null; String err = map.containsKey(ERR) ? map.get(ERR).toString() : null; String line = map.containsKey(LINE) ? map.get(LINE).toString() : null; String className = map.containsKey(CLASS_NAME) ? map.get(CLASS_NAME).toString() : null; @@ -133,8 +134,8 @@ private Boolean isErrorsError(String errorMessage) { /** * Produces {@link TarantoolInternalException} subclasses from the serialized representation * in the format of box.error:unpack, - * @see box_error * + * @see box_error */ public static class TarantoolBoxErrorFactory implements TarantoolErrorFactory { private static final StringValue CODE = ValueFactory.newString("code"); @@ -236,7 +237,7 @@ private Boolean isNetworkError(String code) { /** * Check the error message contains fields of code and message * - * @param code code from box.error + * @param code code from box.error * @param message string message from box.error * @return an {@link Boolean} */ @@ -249,7 +250,6 @@ private Boolean isBoxError(String code, String message) { * The factory is finalizing, i.e. errors passed into * it will always be introverted as appropriate for the given factory * The error is generated in a message that is passed to {@link TarantoolInternalException} - * */ public static class TarantoolUnrecognizedErrorFactory implements TarantoolErrorFactory { public TarantoolUnrecognizedErrorFactory() { diff --git a/src/test/java/io/tarantool/driver/integration/TarantoolErrorsIT.java b/src/test/java/io/tarantool/driver/integration/TarantoolErrorsIT.java index 89ebb77e..38a11de8 100644 --- a/src/test/java/io/tarantool/driver/integration/TarantoolErrorsIT.java +++ b/src/test/java/io/tarantool/driver/integration/TarantoolErrorsIT.java @@ -46,8 +46,8 @@ private ProxyTarantoolTupleClient setupClient() { private RetryingTarantoolTupleClient setupRetryingClient(int retries) { ProxyTarantoolTupleClient client = setupClient(); return new RetryingTarantoolTupleClient(client, - TarantoolRequestRetryPolicies - .byNumberOfAttempts(retries).build()); + TarantoolRequestRetryPolicies + .byNumberOfAttempts(retries).build()); } @Test @@ -58,14 +58,13 @@ void testNetworkError_boxErrorUnpackNoConnection() { client.callForSingleResult("box_error_unpack_no_connection", HashMap.class).get(); fail("Exception must be thrown after last retry attempt."); } catch (Throwable e) { + String message = e.getCause().getMessage(); + assertTrue(e.getCause() instanceof TarantoolInternalNetworkException); - assertTrue(e.getCause().getMessage().contains( - "code: 77\n" + - "message: Connection is not established\n" + - "base_type: ClientError\n" + - "type: ClientError\n" + - "trace:") - ); + assertTrue(message.contains("code: 77")); + assertTrue(message.contains("message: Connection is not established")); + assertTrue(message.contains("type: ClientError")); + assertTrue(message.contains("trace:")); } } @@ -80,14 +79,13 @@ void testNetworkError_boxErrorUnpackTimeout() { String.class).get(); fail("Exception must be thrown after last retry attempt."); } catch (Throwable e) { + String message = e.getCause().getMessage(); + assertTrue(e.getCause() instanceof TarantoolInternalNetworkException); - assertTrue(e.getCause().getMessage().contains( - "code: 78\n" + - "message: Timeout exceeded\n" + - "base_type: ClientError\n" + - "type: ClientError\n" + - "trace:") - ); + assertTrue(message.contains("code: 78")); + assertTrue(message.contains("message: Timeout exceeded")); + assertTrue(message.contains("type: ClientError")); + assertTrue(message.contains("trace:")); } } @@ -102,12 +100,11 @@ void testNetworkError_boxErrorTimeout() { String.class).get(); fail("Exception must be thrown after last retry attempt."); } catch (Throwable e) { + String message = e.getCause().getMessage(); + assertTrue(e.getCause() instanceof TarantoolInternalNetworkException); - assertTrue(e.getCause().getMessage().contains( - "InnerErrorMessage:\n" + - "code: 78\n" + - "message: Timeout exceeded") - ); + assertTrue(message.contains("code: 78")); + assertTrue(message.contains("message: Timeout exceeded")); } } @@ -119,13 +116,13 @@ void testNetworkError_crudErrorTimeout() { client.callForSingleResult("crud_error_timeout", HashMap.class).get(); fail("Exception must be thrown after last retry attempt."); } catch (Throwable e) { + String message = e.getCause().getMessage(); + assertTrue(e.getCause() instanceof TarantoolInternalNetworkException); - assertTrue(e.getCause().getMessage().contains( - "Function returned an error: {\"code\":78," + - "\"base_type\":\"ClientError\"," + - "\"type\":\"ClientError\"," + - "\"message\":\"Timeout exceeded\"," - )); + assertTrue(message.contains("\"code\":78")); + assertTrue(message.contains("\"type\":\"ClientError\"")); + assertTrue(message.contains("\"message\":\"Timeout exceeded\"")); + assertTrue(message.contains("\"trace\":")); } } @@ -137,33 +134,32 @@ void testNonNetworkError_boxErrorUnpack() { client.callForSingleResult("box_error_non_network_error", HashMap.class).get(); fail("Exception must be thrown after last retry attempt."); } catch (Throwable e) { + String message = e.getCause().getMessage(); + assertTrue(e.getCause() instanceof TarantoolInternalException); assertFalse(e.getCause() instanceof TarantoolInternalNetworkException); - assertTrue(e.getCause().getMessage().contains( - "code: 40\n" + - "message: Failed to write to disk\n" + - "base_type: ClientError\n" + - "type: ClientError\n" + - "trace:") - ); + assertTrue(message.contains("code: 40")); + assertTrue(message.contains("message: Failed to write to disk")); + assertTrue(message.contains("type: ClientError")); + assertTrue(message.contains("trace:")); } } - @Test - void testNonNetworkError_boxError() { - try { - ProxyTarantoolTupleClient client = setupClient(); - - client.callForSingleResult("raising_error", HashMap.class).get(); - fail("Exception must be thrown after last retry attempt."); - } catch (Throwable e) { - assertTrue(e.getCause() instanceof TarantoolInternalException); - assertFalse(e.getCause() instanceof TarantoolInternalNetworkException); - assertTrue(e.getCause().getMessage().contains( - "InnerErrorMessage:\n" + - "code: 32\n" + - "message:") - ); - } + @Test + void testNonNetworkError_boxError() { + try { + ProxyTarantoolTupleClient client = setupClient(); + + client.callForSingleResult("raising_error", HashMap.class).get(); + fail("Exception must be thrown after last retry attempt."); + } catch (Throwable e) { + String message = e.getCause().getMessage(); + + assertTrue(e.getCause() instanceof TarantoolInternalException); + assertFalse(e.getCause() instanceof TarantoolInternalNetworkException); + assertTrue(message.contains("InnerErrorMessage:")); + assertTrue(message.contains("code: 32")); + assertTrue(message.contains("message:")); } + } } diff --git a/src/test/resources/cartridge/app/roles/api_router.lua b/src/test/resources/cartridge/app/roles/api_router.lua index 0c08cfd3..d09232a5 100644 --- a/src/test/resources/cartridge/app/roles/api_router.lua +++ b/src/test/resources/cartridge/app/roles/api_router.lua @@ -1,7 +1,7 @@ local vshard = require('vshard') -local cartridge_pool = require('cartridge.pool') local cartridge_rpc = require('cartridge.rpc') local fiber = require('fiber') +local crud = require('crud') local log = require('log') local function get_schema() @@ -31,7 +31,7 @@ local function retrying_function() end local function get_composite_data(id) - local data = vshard.router.callro(vshard.router.bucket_id(id), 'get_composite_data', {id}) + local data = vshard.router.callro(vshard.router.bucket_id(id), 'get_composite_data', { id }) return data end @@ -57,7 +57,7 @@ local function raising_error(message) end local function reset_request_counters() - box.space.request_counters:replace({1, 0}) + box.space.request_counters:replace({ 1, 0 }) end local function get_router_name() @@ -77,15 +77,18 @@ local function long_running_function(values) end end - box.space.request_counters:update(1, {{'+', 'count', 1}}) + -- need using number instead field name as string in update function for compatibility with tarantool 1.10 + box.space.request_counters:update(1, { { '+', 2, 1 } }) log.info('Executing long-running function ' .. tostring(box.space.request_counters:get(1)[2]) .. "(name: " .. disabled_router_name .. "; sleep: " .. seconds_to_sleep .. ")") if get_router_name() == disabled_router_name then - return nil, "Disabled by client; router_name = " .. disabled_router_name + return nil, "Disabled by client; router_name = " .. disabled_router_name + end + if seconds_to_sleep then + fiber.sleep(seconds_to_sleep) end - if seconds_to_sleep then fiber.sleep(seconds_to_sleep) end return true end @@ -111,16 +114,19 @@ local function box_error_non_network_error() end local function crud_error_timeout() - return crud.get("test_space", ('x'):rep(2^27)) + return nil, { class_name = 'SelectError', + err = 'Failed to get next object: GetTupleError: Failed to get tuples from storages: UpdateTuplesError: Failed to select tuples from storages: Call: Failed for 07d14fec-f32b-4b90-aa72-e6755273ad56: Function returned an error: {\"code\":78,\"base_type\":\"ClientError\",\"type\":\"ClientError\",\"message\":\"Timeout exceeded\",\"trace\":[{\"file\":\"builtin\\/box\\/net_box.lua\",\"line\":419}]}', + str = 'SelectError: Failed to get next object: GetTupleError: Failed to get tuples from storages: UpdateTuplesError: Failed to select tuples from storages: Call: Failed for 07d14fec-f32b-4b90-aa72-e6755273ad56: Function returned an error: {\"code\":78,\"base_type\":\"ClientError\",\"type\":\"ClientError\",\"message\":\"Timeout exceeded\",\"trace\":[{\"file\":\"builtin\\/box\\/net_box.lua\",\"line\":419}]}' + } end local function init(opts) box.schema.space.create('request_counters', { - format = {{'id', 'unsigned'}, {'count', 'unsigned'}}, + format = { { 'id', 'unsigned' }, { 'count', 'unsigned' } }, if_not_exists = true }) - box.space.request_counters:create_index('primary', {parts = {'id'}, if_not_exists = true}) + box.space.request_counters:create_index('id', { parts = { 'id' }, if_not_exists = true }) rawset(_G, 'truncate_space', truncate_space)