diff --git a/.circleci/test-server.sh b/.circleci/test-server.sh index f711e4f571e0c..7e57980c718bc 100755 --- a/.circleci/test-server.sh +++ b/.circleci/test-server.sh @@ -356,4 +356,36 @@ if [ "$RUN_WEBHOOK_TESTS" == "true" ] ; then fi +# horizontal scale test + +echo -e "\n<########## TEST GRAPHQL-ENGINE WITH HORIZONTAL SCALING ########>\n" + +HASURA_HS_TEST_DB='postgres://gql_test:@localhost:5432/hs_hge_test' +echo "Installing psql" +apt-get update && apt-get install -y postgresql-client +psql "$HASURA_GRAPHQL_DATABASE_URL" -c "create database hs_hge_test;" + +# start 1st server +"$GRAPHQL_ENGINE" --database-url "$HASURA_HS_TEST_DB" serve >> "$OUTPUT_FOLDER/graphql-engine.log" 2>&1 & PID=$! +wait_for_port 8080 + +# start 2nd server +"$GRAPHQL_ENGINE" --database-url "$HASURA_HS_TEST_DB" serve \ + --server-port 8081 --server-host 0.0.0.0 \ + >> "$OUTPUT_FOLDER/hs-graphql-engine.log" 2>&1 & HS_PID=$! +wait_for_port 8081 + +# run test +pytest -vv --hge-url="$HGE_URL" --pg-url="$HASURA_GRAPHQL_DATABASE_URL" --test-hge-scale-url="http://localhost:8081" test_horizontal_scale.py + +kill -INT $PID +kill -INT $HS_PID +psql "$HASURA_GRAPHQL_DATABASE_URL" -c "drop database hs_hge_test;" +sleep 4 +combine_hpc_reports +unset HASURA_HS_TEST_DB + + +# end horizontal scale test + mv graphql-engine-combined.tix "$OUTPUT_FOLDER/graphql-engine.tix" || true diff --git a/server/tests-py/conftest.py b/server/tests-py/conftest.py index 47a8ba97a6e84..8352475d5eb88 100644 --- a/server/tests-py/conftest.py +++ b/server/tests-py/conftest.py @@ -50,6 +50,13 @@ def pytest_addoption(parser): help="Run Test cases with GraphQL queries being disabled" ) + parser.addoption( + "--test-hge-scale-url", + metavar="", + required=False, + help="Run testcases for horizontal scaling" + ) + @pytest.fixture(scope='session') def hge_ctx(request): @@ -63,6 +70,7 @@ def hge_ctx(request): hge_jwt_conf = request.config.getoption('--hge-jwt-conf') ws_read_cookie = request.config.getoption('--test-ws-init-cookie') metadata_disabled = request.config.getoption('--test-metadata-disabled') + hge_scale_url = request.config.getoption('--test-hge-scale-url') try: hge_ctx = HGECtx( hge_url=hge_url, @@ -73,7 +81,8 @@ def hge_ctx(request): hge_jwt_key_file=hge_jwt_key_file, hge_jwt_conf=hge_jwt_conf, ws_read_cookie=ws_read_cookie, - metadata_disabled=metadata_disabled + metadata_disabled=metadata_disabled, + hge_scale_url=hge_scale_url ) except HGECtxError as e: pytest.exit(str(e)) diff --git a/server/tests-py/context.py b/server/tests-py/context.py index deb0671ef6e6a..b8676fc27d33e 100644 --- a/server/tests-py/context.py +++ b/server/tests-py/context.py @@ -75,7 +75,7 @@ def server_bind(self): class HGECtx: def __init__(self, hge_url, pg_url, hge_key, hge_webhook, webhook_insecure, - hge_jwt_key_file, hge_jwt_conf, metadata_disabled, ws_read_cookie): + hge_jwt_key_file, hge_jwt_conf, metadata_disabled, ws_read_cookie, hge_scale_url): server_address = ('0.0.0.0', 5592) self.resp_queue = queue.Queue(maxsize=1) @@ -118,6 +118,8 @@ def __init__(self, hge_url, pg_url, hge_key, hge_webhook, webhook_insecure, self.ws_read_cookie = ws_read_cookie + self.hge_scale_url = hge_scale_url + result = subprocess.run(['../../scripts/get-version.sh'], shell=False, stdout=subprocess.PIPE, check=True) self.version = result.stdout.decode('utf-8').strip() if not self.metadata_disabled: diff --git a/server/tests-py/queries/horizontal_scale/basic/steps.yaml b/server/tests-py/queries/horizontal_scale/basic/steps.yaml new file mode 100644 index 0000000000000..35da6eff72c57 --- /dev/null +++ b/server/tests-py/queries/horizontal_scale/basic/steps.yaml @@ -0,0 +1,34 @@ +- + operation: + type: bulk + args: + - type: run_sql + args: + sql: | + create table test_t1( + c1 int, + c2 text, + PRIMARY KEY (c1) + ); + - type: track_table + args: + schema: public + name: test_t1 + - type: run_sql + args: + sql: | + insert into test_t1(c1, c2) VALUES(1, 'test'); + validate: + response: + data: + test_t1: + - c1: 1 + c2: test + query: + query: | + query { + test_t1 { + c1 + c2 + } + } \ No newline at end of file diff --git a/server/tests-py/test_horizontal_scale.py b/server/tests-py/test_horizontal_scale.py new file mode 100644 index 0000000000000..3ae7f23331774 --- /dev/null +++ b/server/tests-py/test_horizontal_scale.py @@ -0,0 +1,53 @@ +import pytest +import yaml +import time +import jsondiff + +from validate import json_ordered + + +if not pytest.config.getoption("--test-hge-scale-url"): + pytest.skip("--test-hge-scale-url flag is missing, skipping tests", allow_module_level=True) + + +class TestHorizantalScaleBasic(): + + @pytest.fixture(autouse=True, scope='class') + def transact(self, hge_ctx): + self.teardown = {"type": "clear_metadata", "args": {}} + yield + # teardown + st_code, resp = hge_ctx.v1q(self.teardown) + assert st_code == 200, resp + + def test_horizontal_scale_basic(self, hge_ctx): + with open(self.dir() + "/steps.yaml") as c: + conf = yaml.load(c) + + assert isinstance(conf, list) == True, 'Not an list' + for _, step in enumerate(conf): + # execute operation on 1st server + st_code, resp = hge_ctx.v1q(step['operation']) + assert st_code == 200, resp + + # wait for x sec + time.sleep(0.3) + # validate data on 2nd server + response = hge_ctx.http.post( + hge_ctx.hge_scale_url + "/v1alpha1/graphql", + json=step['validate']['query'] + ) + st_code = response.status_code + resp = response.json() + assert st_code == 200, resp + + if 'response' in step['validate']: + assert json_ordered(resp) == json_ordered(step['validate']['response']), yaml.dump({ + 'response': resp, + 'expected': step['validate']['response'], + 'diff': jsondiff.diff(step['validate']['response'], resp) + }) + + @classmethod + def dir(cls): + return 'queries/horizontal_scale/basic'