Add virtual table system_views.snapshots

patch by Maxwell Guo; reviewed by Stefan Miklosovic and Paulo Motta for CASSANDRA-18102

Co-authored-by: Stefan Miklosovic <smiklosovic@apache.org>
This commit is contained in:
maxwellguo 2023-03-09 11:18:14 +08:00 committed by Stefan Miklosovic
parent eee211fdbb
commit fc9efd6357
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
6 changed files with 191 additions and 1 deletions

View File

@ -1,4 +1,5 @@
5.0
* Add system_views.snapshots virtual table (CASSANDRA-18102)
* Update OpenHFT dependencies (chronicle-queue, chronicle-core, chronicle-bytes, chronicle-wire, chronicle-threads) (CASSANDRA-18049)
* Remove org.apache.cassandra.hadoop code (CASSANDRA-18323)
* Remove deprecated CQL functions dateOf and unixTimestampOf (CASSANDRA-18328)

View File

@ -131,6 +131,7 @@ New features
`maxTimestamp - minTimestamp`, effectively non-zero for SSTables produced by TimeWindowCompactionStrategy.
- Added local read/write ratio to tablestats.
- Added system_views.max_sstable_size and system_views.max_sstable_duration tables.
- Added virtual table system_views.snapshots to see all snapshots from CQL shell.
Upgrading
---------

View File

@ -0,0 +1,82 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.db.virtual;
import java.util.Date;
import org.apache.cassandra.db.marshal.BooleanType;
import org.apache.cassandra.db.marshal.LongType;
import org.apache.cassandra.db.marshal.TimestampType;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.dht.LocalPartitioner;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.snapshot.TableSnapshot;
public class SnapshotsTable extends AbstractVirtualTable
{
private static final String NAME = "name";
private static final String KEYSPACE_NAME = "keyspace_name";
private static final String TABLE_NAME = "table_name";
private static final String TRUE_SIZE = "true_size";
private static final String SIZE_ON_DISK = "size_on_disk";
private static final String CREATED_AT = "created_at";
private static final String EXPIRES_AT = "expires_at";
private static final String EPHEMERAL = "ephemeral";
SnapshotsTable(String keyspace)
{
super(TableMetadata.builder(keyspace, "snapshots")
.comment("available snapshots")
.kind(TableMetadata.Kind.VIRTUAL)
.partitioner(new LocalPartitioner(UTF8Type.instance))
.addPartitionKeyColumn(NAME, UTF8Type.instance)
.addClusteringColumn(KEYSPACE_NAME, UTF8Type.instance)
.addClusteringColumn(TABLE_NAME, UTF8Type.instance)
.addRegularColumn(TRUE_SIZE, LongType.instance)
.addRegularColumn(SIZE_ON_DISK, LongType.instance)
.addRegularColumn(CREATED_AT, TimestampType.instance)
.addRegularColumn(EXPIRES_AT, TimestampType.instance)
.addRegularColumn(EPHEMERAL, BooleanType.instance)
.build());
}
@Override
public DataSet data()
{
SimpleDataSet result = new SimpleDataSet(metadata());
for (TableSnapshot tableSnapshot : StorageService.instance.snapshotManager.loadSnapshots())
{
SimpleDataSet row = result.row(tableSnapshot.getTag(),
tableSnapshot.getKeyspaceName(),
tableSnapshot.getTableName())
.column(TRUE_SIZE, tableSnapshot.computeTrueSizeBytes())
.column(SIZE_ON_DISK, tableSnapshot.computeSizeOnDiskBytes())
.column(CREATED_AT, new Date(tableSnapshot.getCreatedAt().toEpochMilli()));
if (tableSnapshot.isExpiring())
row.column(EXPIRES_AT, new Date(tableSnapshot.getExpiresAt().toEpochMilli()));
row.column(EPHEMERAL, tableSnapshot.isEphemeral());
}
return result;
}
}

View File

@ -49,6 +49,7 @@ public final class SystemViewsKeyspace extends VirtualKeyspace
.add(new GossipInfoTable(VIRTUAL_VIEWS))
.add(new QueriesTable(VIRTUAL_VIEWS))
.add(new LogMessagesTable(VIRTUAL_VIEWS))
.add(new SnapshotsTable(VIRTUAL_VIEWS))
.addAll(LocalRepairTables.getAll(VIRTUAL_VIEWS))
.build());
}

View File

@ -311,7 +311,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
private final List<Runnable> preShutdownHooks = new ArrayList<>();
private final List<Runnable> postShutdownHooks = new ArrayList<>();
private final SnapshotManager snapshotManager = new SnapshotManager();
public final SnapshotManager snapshotManager = new SnapshotManager();
public static final StorageService instance = new StorageService();

View File

@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.db.virtual;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.Date;
import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.apache.cassandra.config.DurationSpec;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.Clock;
public class SnapshotsTableTest extends CQLTester
{
private static final String KS_NAME = "vts";
private static final String SNAPSHOT_TTL = "snapshotTtl";
private static final String SNAPSHOT_NO_TTL = "snapshotNoTtl";
private static final DurationSpec.IntSecondsBound ttl = new DurationSpec.IntSecondsBound("4h");
@Before
public void before() throws Throwable
{
SnapshotsTable vtable = new SnapshotsTable(KS_NAME);
VirtualKeyspaceRegistry.instance.register(new VirtualKeyspace(KS_NAME, ImmutableList.of(vtable)));
createTable("CREATE TABLE %s (pk int, ck int, PRIMARY KEY (pk, ck))");
for (int i = 0; i != 10; ++i)
execute("INSERT INTO %s (pk, ck) VALUES (?, ?)", i, i);
flush();
}
@After
public void after()
{
StorageService.instance.clearSnapshot(Collections.emptyMap(), SNAPSHOT_NO_TTL, KEYSPACE);
StorageService.instance.clearSnapshot(Collections.emptyMap(), SNAPSHOT_TTL, KEYSPACE);
schemaChange(String.format("DROP TABLE %s", KEYSPACE + "." + currentTable()));
}
@Test
public void testSnapshots() throws Throwable
{
Instant now = Instant.ofEpochMilli(Clock.Global.currentTimeMillis()).truncatedTo(ChronoUnit.MILLIS);
Date createdAt = new Date(now.toEpochMilli());
Date expiresAt = new Date(now.plusSeconds(ttl.toSeconds()).toEpochMilli());
getCurrentColumnFamilyStore(KEYSPACE).snapshot(SNAPSHOT_NO_TTL, null, false, false, null, null, now);
getCurrentColumnFamilyStore(KEYSPACE).snapshot(SNAPSHOT_TTL, null, false, false, ttl, null, now);
// query all from snapshots virtual table
UntypedResultSet result = execute("SELECT name, keyspace_name, table_name, created_at, expires_at, ephemeral FROM vts.snapshots");
assertRowsIgnoringOrder(result,
row(SNAPSHOT_NO_TTL, KEYSPACE, currentTable(), createdAt, null, false),
row(SNAPSHOT_TTL, KEYSPACE, currentTable(), createdAt, expiresAt, false));
// query with conditions
result = execute("SELECT name, keyspace_name, table_name, created_at, expires_at, ephemeral FROM vts.snapshots where ephemeral = false");
assertRows(result,
row(SNAPSHOT_NO_TTL, KEYSPACE, currentTable(), createdAt, null, false),
row(SNAPSHOT_TTL, KEYSPACE, currentTable(), createdAt, expiresAt, false));
result = execute("SELECT name, keyspace_name, table_name, created_at, expires_at, ephemeral FROM vts.snapshots where size_on_disk > 1000");
assertRows(result,
row(SNAPSHOT_NO_TTL, KEYSPACE, currentTable(), createdAt, null, false),
row(SNAPSHOT_TTL, KEYSPACE, currentTable(), createdAt, expiresAt, false));
result = execute("SELECT name, keyspace_name, table_name, created_at, expires_at, ephemeral FROM vts.snapshots where name = ?", SNAPSHOT_TTL);
assertRows(result,
row(SNAPSHOT_TTL, KEYSPACE, currentTable(), createdAt, expiresAt, false));
// clear some snapshots
StorageService.instance.clearSnapshot(Collections.emptyMap(), SNAPSHOT_NO_TTL, KEYSPACE);
result = execute("SELECT name, keyspace_name, table_name, created_at, expires_at, ephemeral FROM vts.snapshots");
assertRowsIgnoringOrder(result,
row(SNAPSHOT_TTL, KEYSPACE, currentTable(), createdAt, expiresAt, false));
}
}