예제 #1
0
def test_storage_options_nonexistent_param(cql, scylla_only):
    ksdef = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'S3', 'bucket' : '42', 'endpoint' : 'localhost', 'superfluous' : 'info' }"
    with pytest.raises(InvalidRequest):
        with new_test_keyspace(cql, ksdef):
            pass
    ksdef = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'LOCAL', 'superfluous' : 'info' }"
    with pytest.raises(InvalidRequest):
        with new_test_keyspace(cql, ksdef):
            pass
예제 #2
0
def test_storage_service_keyspaces(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        resp_user = rest_api.send("GET", "storage_service/keyspaces",
                                  {"type": "user"})
        resp_user.raise_for_status()
        keyspaces_user = resp_user.json()
        assert keyspace in keyspaces_user
        assert all(not ks.startswith("system") for ks in keyspaces_user)

        resp_nls = rest_api.send("GET", "storage_service/keyspaces",
                                 {"type": "non_local_strategy"})
        resp_nls.raise_for_status()
        assert keyspace in resp_nls.json()

        resp_all = rest_api.send("GET", "storage_service/keyspaces",
                                 {"type": "all"})
        resp_all.raise_for_status()
        assert keyspace in resp_all.json()

        resp = rest_api.send("GET", "storage_service/keyspaces")
        resp.raise_for_status()
        assert keyspace in resp.json()
예제 #3
0
def test_storage_service_keyspace_scrub(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        with new_test_table(cql, keyspace, "a int, PRIMARY KEY (a)") as t0:
            with new_test_table(cql, keyspace, "a int, PRIMARY KEY (a)") as t1:
                test_tables = [t0.split('.')[1], t1.split('.')[1]]

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}")
                resp.raise_for_status()

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}",
                    {"cf": f"{test_tables[1]}"})
                resp.raise_for_status()

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}",
                    {"cf": f"{test_tables[0]},{test_tables[1]}"})
                resp.raise_for_status()

                # non-existing table
                resp = rest_api.send(
                    "POST", f"storage_service/keyspace_scrub/{keyspace}",
                    {"cf": f"{test_tables[0]},XXX"})
                assert resp.status_code == requests.codes.not_found
예제 #4
0
def test_storage_service_keyspace_bad_param(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        # Url must include the keyspace param.
        resp = rest_api.send("GET", f"storage_service/keyspace_scrub")
        assert resp.status_code == requests.codes.not_found

        # Url must include the keyspace param.
        # It cannot be given as an optional param
        resp = rest_api.send("GET", f"storage_service/keyspace_scrub",
                             {"keyspace": "{keyspace}"})
        assert resp.status_code == requests.codes.not_found

        # Optional param cannot use the same name as a mandatory (positional, in url) param.
        resp = rest_api.send("GET",
                             f"storage_service/keyspace_scrub/{keyspace}",
                             {"keyspace": "{keyspace}"})
        assert resp.status_code == requests.codes.bad_request

        # Unknown parameter (See https://github.com/scylladb/scylla/pull/10090)
        resp = rest_api.send("GET",
                             f"storage_service/keyspace_scrub/{keyspace}",
                             {"foo": "bar"})
        assert resp.status_code == requests.codes.bad_request
예제 #5
0
def test_storage_service_snapshot_mv_si(cql, this_dc, rest_api):
    resp = rest_api.send("GET", "storage_service/snapshots")
    resp.raise_for_status()

    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        schema = 'p int, v text, primary key (p)'
        with new_test_table(cql, keyspace, schema) as table:
            with new_materialized_view(
                    cql, table, '*', 'v, p',
                    'v is not null and p is not null') as mv:
                try:
                    with new_test_snapshot(rest_api, keyspace,
                                           mv.split('.')[1]) as snap:
                        pytest.fail(
                            f"Snapshot of materialized view {mv} should have failed"
                        )
                except requests.HTTPError:
                    pass

            with new_secondary_index(cql, table, 'v') as si:
                try:
                    with new_test_snapshot(rest_api, keyspace,
                                           si.split('.')[1]) as snap:
                        pytest.fail(
                            f"Snapshot of secondary index {si} should have failed"
                        )
                except requests.HTTPError:
                    pass
예제 #6
0
def test_alter_keyspace(cql):
    with new_test_keyspace(
            cql,
            "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }"
    ) as keyspace:
        cql.execute(
            f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'SimpleStrategy', 'replication_factor' : 3 }} AND DURABLE_WRITES = false"
        )
예제 #7
0
def test_alter_keyspace(cql, this_dc):
    with new_test_keyspace(
            cql,
            "WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', '" +
            this_dc + "' : 1 }") as keyspace:
        cql.execute(
            f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 3 }} AND DURABLE_WRITES = false"
        )
예제 #8
0
def test_storage_options_alter_type(cql, scylla_only):
    ksdef = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'LOCAL' }"
    with new_test_keyspace(cql, ksdef) as keyspace:
         # It's not fine to change the storage type
        ksdef_local = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'S3', 'bucket' : '/b1', 'endpoint': 'localhost'}"
        with pytest.raises(InvalidRequest):
            res = cql.execute(f"ALTER KEYSPACE {keyspace} {ksdef_local}")
예제 #9
0
def test_alter_keyspace_nonexistent_dc(cql):
    with new_test_keyspace(
            cql,
            "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }"
    ) as keyspace:
        with pytest.raises(ConfigurationException):
            cql.execute(
                f"ALTER KEYSPACE {keyspace} WITH replication = {{ 'class' : 'NetworkTopologyStrategy', 'nonexistentdc' : 1 }}"
            )
예제 #10
0
def test_alter_keyspace_invalid(cql):
    with new_test_keyspace(cql, "WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1 }") as keyspace:
        with pytest.raises(ConfigurationException):
            cql.execute(f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'NoSuchStrategy' }}")
        # SimpleStrategy, if not outright forbidden, requires a
        # replication_factor option.
        with pytest.raises(ConfigurationException):
            cql.execute(f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'SimpleStrategy' }}")
        with pytest.raises(ConfigurationException):
            cql.execute(f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 'foo' }}")
예제 #11
0
def test_concurrent_create_and_drop_keyspace(cql, this_dc):
    ksdef = "WITH REPLICATION = { 'class' : 'NetworkTopologyStrategy', '" + this_dc + "' : 1 }"
    cfdef = "(a int PRIMARY KEY)"
    with new_test_keyspace(cql, ksdef) as keyspace:
        # The more iterations we do, the higher the chance of reproducing
        # this issue. On my laptop, count = 40 reproduces the bug every time.
        # Lower numbers have some chance of not catching the bug. If this
        # issue starts to xpass, we may need to increase the count.
        count = 40

        def drops(count):
            for i in range(count):
                try:
                    cql.execute(f"DROP KEYSPACE {keyspace}")
                except Exception as e:
                    print(e)
                else:
                    print("drop successful")

        def creates(count):
            for i in range(count):
                try:
                    cql.execute(f"CREATE KEYSPACE {keyspace} {ksdef}")
                    print("create keyspace successful")
                    # Create a table in this keyspace. This creation may
                    # race with deletion of the entire keyspace by the
                    # parallel thread. Reproducing #8968 requires this
                    # operation - just creating and deleting the keyspace
                    # without anything in it did not reproduce the problem.
                    cql.execute(f"CREATE TABLE {keyspace}.xyz {cfdef}")
                except Exception as e:
                    print(e)
                else:
                    print("create table successful")

        t1 = Thread(target=drops, args=[count])
        t2 = Thread(target=creates, args=[count])
        t1.start()
        t2.start()
        t1.join()
        t2.join()
        # At this point, the keyspace should either exist, or not exist.
        # So CREATE KEYSPACE IF NOT EXIST should ensure it does exist,
        # and then one DROP KEYSPACE should succeed, a second one should
        # fail, and finally we can recreate the keyspace as new_test_keyspace
        # expects it.
        # If any of the following statements fail, it means we reached an
        # invalid state. This is issue #8968.
        cql.execute(f"CREATE KEYSPACE IF NOT EXISTS {keyspace} {ksdef}")
        cql.execute(f"DROP KEYSPACE {keyspace}")
        # See explanation above how different versions of Cassandra and
        # Scylla produce different errors when dropping a non-existent ks:
        with pytest.raises((InvalidRequest, ConfigurationException)):
            cql.execute(f"DROP KEYSPACE {keyspace}")
        cql.execute(f"CREATE KEYSPACE {keyspace} {ksdef}")
예제 #12
0
def test_alter_keyspace_invalid(cql):
    with new_test_keyspace(
            cql,
            "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }"
    ) as keyspace:
        with pytest.raises(ConfigurationException):
            cql.execute(
                f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'NoSuchStrategy' }}"
            )
        with pytest.raises(ConfigurationException):
            cql.execute(
                f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'SimpleStrategy' }}"
            )
        with pytest.raises(ConfigurationException):
            cql.execute(
                f"ALTER KEYSPACE {keyspace} WITH REPLICATION = {{ 'class' : 'SimpleStrategy', 'replication_factor' : 'foo' }}"
            )
예제 #13
0
def test_toppartitions_pk_needs_escaping(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        with new_test_table(cql, keyspace, "p text PRIMARY KEY") as table:
            # Use a newline character as part of the partition key pk. When
            # toppartitions later returns it, it must escape it (as pk_json)
            # or yield an invalid JSON with a literal newline in a string.
            pk = 'hi\nhello'
            pk_json = r'hi\nhello'

            # Unfortunately, the toppartitions API doesn't let us mark the
            # beginning and end of the sampling period. Instead we need to
            # start the toppartitions for a predefined period, and in
            # parallel, make the request. Sad.
            def toppartitions():
                ks, cf = table.split('.')
                resp = rest_api.send('GET', 'storage_service/toppartitions', {
                    'table_filters': f'{ks}:{cf}',
                    'duration': '1000'
                })
                assert resp.ok
                # resp.json() will raise an error if not valid JSON
                resp.json()
                assert pk_json in resp.text

            def insert():
                # We need to wait enough time for the toppartitions request
                # to have been sent, but unfortunately we don't know when
                # this happens because the request doesn't return until the
                # "duration" ends. So we hope 0.5 seconds is enough.
                # TODO: we can use the log to check when the toppartitions
                # request was received.
                time.sleep(0.5)
                stmt = cql.prepare(f"INSERT INTO {table} (p) VALUES (?)")
                cql.execute(stmt, [pk])

            t1 = ThreadWrapper(target=toppartitions)
            t2 = ThreadWrapper(target=insert)
            t1.start()
            t2.start()
            t1.join()
            t2.join()
예제 #14
0
def test_storage_service_flush(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        with new_test_table(cql, keyspace, "p text PRIMARY KEY") as table0:
            ks, t0 = table0.split('.')
            stmt = cql.prepare(f"INSERT INTO {table0} (p) VALUES (?)")
            cql.execute(stmt, ["pk0"])
            with new_test_table(cql, keyspace, "p text PRIMARY KEY") as table1:
                _, t1 = table1.split('.')
                stmt = cql.prepare(f"INSERT INTO {table1} (p) VALUES (?)")
                cql.execute(stmt, ["pk1"])

                # test the keyspace_flush doesn't produce any errors when called on existing keyspace and table(s)
                resp = rest_api.send("POST",
                                     f"storage_service/keyspace_flush/{ks}")
                resp.raise_for_status()

                resp = rest_api.send("POST",
                                     f"storage_service/keyspace_flush/{ks}",
                                     {"cf": f"{t0}"})
                resp.raise_for_status()

                resp = rest_api.send("POST",
                                     f"storage_service/keyspace_flush/{ks}",
                                     {"cf": f"{t0},{t1}"})
                resp.raise_for_status()

                # test error when keyspace_flush is called on non-existing keyspace or table(s)
                resp = rest_api.send(
                    "POST", f"storage_service/keyspace_flush/no_such_keyspace")
                assert resp.status_code == requests.codes.bad_request

                resp = rest_api.send("POST",
                                     f"storage_service/keyspace_flush/{ks}",
                                     {"cf": f"no_such_table"})
                assert resp.status_code == requests.codes.bad_request

                resp = rest_api.send("POST",
                                     f"storage_service/keyspace_flush/{ks}",
                                     {"cf": f"{t0},no_such_table,{t1}"})
                assert resp.status_code == requests.codes.bad_request
예제 #15
0
def test_materialized_view_pre_scrub_snapshot(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        schema = 'p int, v text, primary key (p)'
        with new_test_table(cql, keyspace, schema) as table:
            stmt = cql.prepare(f"INSERT INTO {table} (p, v) VALUES (?, ?)")
            cql.execute(stmt, [0, 'hello'])

            with new_materialized_view(
                    cql, table, '*', 'v, p',
                    'v is not null and p is not null') as mv:
                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}")
                resp.raise_for_status()

            with new_secondary_index(cql, table, 'v') as si:
                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}")
                resp.raise_for_status()
예제 #16
0
def test_storage_service_keyspace_scrub_mode(cql, this_dc, rest_api):
    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace:
        with new_test_table(cql, keyspace, "a int, PRIMARY KEY (a)") as t0:
            with new_test_table(cql, keyspace, "a int, PRIMARY KEY (a)") as t1:
                test_tables = [t0.split('.')[1], t1.split('.')[1]]

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}", {
                        "cf": f"{test_tables[0]}",
                        "scrub_mode": "VALIDATE"
                    })
                resp.raise_for_status()

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}", {
                        "cf": f"{test_tables[0]}",
                        "scrub_mode": "XXX"
                    })
                assert resp.status_code == requests.codes.bad_request

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}", {
                        "cf": f"{test_tables[0]}",
                        "quarantine_mode": "ONLY"
                    })
                resp.raise_for_status()

                resp = rest_api.send(
                    "GET", f"storage_service/keyspace_scrub/{keyspace}", {
                        "cf": f"{test_tables[0]}",
                        "quarantine_mode": "YYY"
                    })
                assert resp.status_code == requests.codes.bad_request
예제 #17
0
def test_storage_options_local(cql, scylla_only):
    ksdef = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'LOCAL' }"
    with new_test_keyspace(cql, ksdef) as keyspace:
        res = cql.execute(f"SELECT * FROM system_schema.scylla_keyspaces WHERE keyspace_name = '{keyspace}'")
        assert not res.all()
예제 #18
0
def test_storage_options_required_param(cql, scylla_only):
    ksdef = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'S3', 'bucket' : '42' }"
    with pytest.raises(InvalidRequest):
        with new_test_keyspace(cql, ksdef):
            pass
예제 #19
0
def test_storage_service_snapshot(cql, this_dc, rest_api):
    resp = rest_api.send("GET", "storage_service/snapshots")
    resp.raise_for_status()

    def verify_snapshot_details(expected):
        resp = rest_api.send("GET", "storage_service/snapshots")
        found = False
        for data in resp.json():
            if data['key'] == expected['key']:
                assert not found
                found = True
                sort_key = lambda v: f"{v['ks']}-{v['cf']}"
                value = sorted([
                    v
                    for v in data['value'] if not v['ks'].startswith('system')
                ],
                               key=sort_key)
                expected_value = sorted(expected['value'], key=sort_key)
                assert len(value) == len(
                    expected_value
                ), f"length mismatch: expected {expected_value} but got {value}"
                for i in range(len(value)):
                    v = value[i]
                    # normalize `total` and `live`
                    # since we care only if they are zero or not
                    v['total'] = 1 if v['total'] else 0
                    v['live'] = 1 if v['live'] else 0
                    ev = expected_value[i]
                    assert v == ev
        assert found, f"key='{expected['key']}' not found in {resp.json()}"

    with new_test_keyspace(
            cql,
            f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
    ) as keyspace0:
        with new_test_table(cql, keyspace0, "p text PRIMARY KEY") as table00:
            ks0, cf00 = table00.split('.')
            stmt = cql.prepare(f"INSERT INTO {table00} (p) VALUES (?)")
            cql.execute(stmt, ["pk0"])

            # single keyspace / table
            with new_test_snapshot(rest_api, ks0, cf00) as snapshot0:
                verify_snapshot_details({
                    'key':
                    snapshot0,
                    'value': [{
                        'ks': ks0,
                        'cf': cf00,
                        'total': 1,
                        'live': 0
                    }]
                })

                cql.execute(f"TRUNCATE {table00}")
                verify_snapshot_details({
                    'key':
                    snapshot0,
                    'value': [{
                        'ks': ks0,
                        'cf': cf00,
                        'total': 1,
                        'live': 1
                    }]
                })

            with new_test_table(cql, keyspace0,
                                "p text PRIMARY KEY") as table01:
                _, cf01 = table01.split('.')
                stmt = cql.prepare(f"INSERT INTO {table01} (p) VALUES (?)")
                cql.execute(stmt, ["pk1"])

                # single keyspace / multiple tables
                with new_test_snapshot(rest_api, ks0,
                                       [cf00, cf01]) as snapshot1:
                    verify_snapshot_details({
                        'key':
                        snapshot1,
                        'value': [{
                            'ks': ks0,
                            'cf': cf00,
                            'total': 0,
                            'live': 0
                        }, {
                            'ks': ks0,
                            'cf': cf01,
                            'total': 1,
                            'live': 0
                        }]
                    })

                with new_test_keyspace(
                        cql,
                        f"WITH REPLICATION = {{ 'class' : 'NetworkTopologyStrategy', '{this_dc}' : 1 }}"
                ) as keyspace1:
                    with new_test_table(cql, keyspace1,
                                        "p text PRIMARY KEY") as table10:
                        ks1, cf10 = table10.split('.')

                        # multiple keyspaces
                        with new_test_snapshot(rest_api,
                                               [ks0, ks1]) as snapshot2:
                            verify_snapshot_details({
                                'key':
                                snapshot2,
                                'value': [{
                                    'ks': ks0,
                                    'cf': cf00,
                                    'total': 0,
                                    'live': 0
                                }, {
                                    'ks': ks0,
                                    'cf': cf01,
                                    'total': 1,
                                    'live': 0
                                }, {
                                    'ks': ks1,
                                    'cf': cf10,
                                    'total': 0,
                                    'live': 0
                                }]
                            })

                        # all keyspaces
                        with new_test_snapshot(rest_api, ) as snapshot3:
                            verify_snapshot_details({
                                'key':
                                snapshot3,
                                'value': [{
                                    'ks': ks0,
                                    'cf': cf00,
                                    'total': 0,
                                    'live': 0
                                }, {
                                    'ks': ks0,
                                    'cf': cf01,
                                    'total': 1,
                                    'live': 0
                                }, {
                                    'ks': ks1,
                                    'cf': cf10,
                                    'total': 0,
                                    'live': 0
                                }]
                            })
예제 #20
0
def test_storage_options_unknown_type(cql, scylla_only):
    ksdef = "WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : '1' } " \
            "AND STORAGE = { 'type' : 'S4', 'bucket' : '42', 'endpoint' : 'localhost' }"
    with pytest.raises(InvalidRequest):
        with new_test_keyspace(cql, ksdef):
            pass