Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mark RangeRequest.all() as @RestrictedApi #7375

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* (c) Copyright 2024 Palantir Technologies Inc. All rights reserved.
*
* Licensed 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 com.palantir.atlasdb.keyvalue.api;

/**
* Mark that this range request has been thought about and explicitly decided to be ok.
*/
public @interface AllowedRangeRequest {
Copy link
Contributor

@sverma30 sverma30 Oct 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if having an enum field for high level categorisation would be helpful for future analysis for why services are relying on rangerequests. Like background task, search use case, backfill, etc?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting! could be useful, yes.

/**
* Explain why this is ok - i.e. what rate limiting is in place on calling codepaths/how we
* know that the overall number of rows read will be small, etc.
*/
String justification();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: newline

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.primitives.UnsignedBytes;
import com.google.errorprone.annotations.RestrictedApi;
import com.palantir.atlasdb.encoding.PtBytes;
import com.palantir.atlasdb.transaction.api.Transaction;
import com.palantir.common.annotation.Immutable;
Expand Down Expand Up @@ -60,6 +61,11 @@ public final class RangeRequest implements Serializable {
private final boolean reverse;
private transient int hashCode = 0;

public static final String RANGE_REQUEST_ALLOWED_ON_PATH = ".*/schema/generated/.*"
+ "|.*/integrationInput/.*"
+ "|.*/atlasdb-ete-test-utils/.*"
+ "|.*/src/test/java/.*";

/**
* Returns a {@link Builder} instance, a helper class
* for instantiating immutable <code>RangeRequest</code>
Expand All @@ -77,6 +83,11 @@ public static Builder reverseBuilder() {
return new Builder(true);
}

@RestrictedApi(
explanation = "Full range requests create a lot of load on cassandra, which can hurt the overall cluster ",
allowlistAnnotations = AllowedRangeRequest.class,
allowedOnPath = RANGE_REQUEST_ALLOWED_ON_PATH)
@SuppressWarnings("RestrictedApi")
public static RangeRequest all() {
return builder().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.palantir.atlasdb.cassandra.CassandraMutationTimestampProviders;
import com.palantir.atlasdb.cassandra.CassandraServersConfigs.CassandraServersConfig;
import com.palantir.atlasdb.encoding.PtBytes;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.AsyncKeyValueService;
import com.palantir.atlasdb.keyvalue.api.BatchColumnRangeSelection;
import com.palantir.atlasdb.keyvalue.api.CandidateCellForSweeping;
Expand Down Expand Up @@ -1691,6 +1692,7 @@ private static String schemaChangeDescriptionForPutMetadataForTables(Collection<
}

@Override
@AllowedRangeRequest(justification = "RangeRequest.all only invoked for equality check")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but is truncateTables bad/expensive?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method checks if the request is equal to rangerequest.all() and in that case, as an optimisation, truncated the table instead of writing deletes on the full range. Truncate does not create tombstones so in that perspective it's better, but not sure about the performance. @jeremyk-91 do you know if this is expensive?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's make it consistent with the other place where the justification is same: "a full range request is constructed only for an equality check"

Copy link
Contributor

@jeremyk-91 jeremyk-91 Oct 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With regard to truncate: I'd check with @inespot, but my understanding is that yes, truncate is much cheaper than just deleting. It does require all Cassandra nodes to be up and the coordinator needs to get an ack from each node that the data was deleted, but this should be comparatively fast. Also, in practice, deletes generally require consistency all and will similarly be affected by single node degradation (strictly speaking, there is an edge case where deletion is probably faster, outlined below in (*), but I don't think we care in practice).

(*) The "normal" method involves reading all cells (which only requires consistency quorum) and then deleting these cells. That does require consistency all; but technically if there's a situation where no 2 nodes owning the same data are down (or are slow), and for rows that actually exist in the table, no single node owning the same data is down (or is slow), legacy works (or is fast) while truncate does not (or is slow). This is more of an academic edge case, though.

public void deleteRange(final TableReference tableRef, final RangeRequest range) {
if (range.equals(RangeRequest.all())) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.common.collect.Multimaps;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.encoding.PtBytes;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.KeyAlreadyExistsException;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
Expand Down Expand Up @@ -76,6 +77,7 @@ public int execute(AtlasDbServices services) {
return 0;
}

@AllowedRangeRequest(justification = "this is a cli and so should only ever be invoked manually")
public static void run(KeyValueService kvs, PrintWriter output, int batchSize) {
Context context = new Context(kvs, output, batchSize);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.common.collect.Multiset;
import com.google.common.collect.Sets;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.ClusterAvailabilityStatus;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
Expand Down Expand Up @@ -161,6 +162,7 @@ public void putWithTimestamps(TableReference tableRef, Multimap<Cell, Value> cel
}

@Override
@AllowedRangeRequest(justification = "a full range request is constructed only for an equality check")
public void deleteRange(TableReference tableRef, RangeRequest range) {
delegate().deleteRange(tableRef, range);
if (isEnabled.get()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.common.collect.Streams;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.encoding.PtBytes;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.Namespace;
Expand Down Expand Up @@ -107,6 +108,7 @@ public boolean doesNotExistBeforeTimestamp(long id, long ts) {
return kvs.get().get(tableRef, ImmutableMap.of(cell, ts + 1)).isEmpty();
}

@AllowedRangeRequest(justification = "test")
public List<Todo> getTodoList() {
ImmutableList<RowResult<byte[]>> results = transactionManager.runTaskWithRetry(transaction -> {
BatchingVisitable<RowResult<byte[]>> rowResultBatchingVisitable =
Expand Down Expand Up @@ -235,6 +237,7 @@ private Value getValueForEntry(TableReference tableRef, Map.Entry<Cell, Value> e
.get(entry.getKey());
}

@AllowedRangeRequest(justification = "test")
private Set<Cell> getAllCells(TableReference tableRef) {
try (ClosableIterator<RowResult<Value>> iterator =
kvs.get().getRange(tableRef, RangeRequest.all(), Long.MAX_VALUE)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
Expand Down Expand Up @@ -47,6 +48,7 @@ public V1TransactionsTableRangeDeleter(
}

@Override
@AllowedRangeRequest(justification = "???")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any context on what's calling this/why doing this deletion is ok?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jeremyk-91 is this on the restore path?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, only used on the restore path and for the transaction table, which in normal operation does not have any tombstones written. (Yes, there is a risk if you restore a stack twice in succession with no intervening compaction; should we make an internal note of this on SOPs or similar?)

public void deleteRange(TimestampRange commitTimestampRange) {
byte[] startBytes = TransactionConstants.getValueForTimestamp(
startTimestamp.orElse(TransactionConstants.LOWEST_POSSIBLE_START_TS));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
import com.palantir.atlasdb.keyvalue.api.TableReference;
Expand Down Expand Up @@ -89,6 +90,7 @@ private Set<TableReference> getConservativeTables(Set<TableReference> tables) {
}

@Override
@AllowedRangeRequest(justification = "deleting a range is cheaper than reading one")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually true?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

truncating the table is a cheaper option to deleting the full range

public void deleteAllRowsInTables(Set<TableReference> tables) {
execute(tables, filtered -> filtered.forEach(table -> kvs.deleteRange(table, RangeRequest.all())));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.palantir.atlasdb.sweep.queue.clear;

import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
import com.palantir.atlasdb.keyvalue.api.TableReference;
Expand Down Expand Up @@ -59,6 +60,7 @@ public void truncateTables(Set<TableReference> tables) {

// Some people use delete(RangeRequest.all()) instead of truncate because truncates negatively affect Oracle backups
@Override
@AllowedRangeRequest(justification = "Doesn't actually result in a range request - calls deleteAllRowsInTables")
public void deleteRange(TableReference table, RangeRequest range) {
if (range.equals(RangeRequest.all())) {
tableClearer.deleteAllRowsInTables(table);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.primitives.UnsignedBytes;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.ColumnSelection;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
Expand Down Expand Up @@ -163,6 +164,7 @@ private static PartialCopyStats copyInternal(
return stats;
}

@AllowedRangeRequest(justification = "???")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size estimator is ok because..? we only ever read one batch?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh this is spicy, this in theory can be very expensive if for reading that one batch we need to contact all cassandra nodes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Today at least: only used by the large internal product, which is not generally deployed with Cassandra.

(This probably should itself be restricted, though probably something for another day...)

public static long estimateSize(
Transaction transaction, TableReference table, int batchSize, Function<byte[], byte[]> uniformizer) {
final AtomicLong estimate = new AtomicLong();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
import com.palantir.atlasdb.keyvalue.api.TimestampRangeDelete;
Expand All @@ -39,6 +40,7 @@ private Object doDelete(RegeneratingTable<Multimap<Cell, Long>> table) {
return table.getTableCells();
}

@AllowedRangeRequest(justification = "just a benchmark")
private Object doDeleteRange(ConsecutiveNarrowTable table, int numBatches) {
Iterable<RangeRequest> rangeRequests = numBatches == 1
? ImmutableList.of(RangeRequest.all())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.google.common.primitives.UnsignedBytes;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.encoding.PtBytes;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.BatchColumnRangeSelection;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.CheckAndSetException;
Expand Down Expand Up @@ -87,6 +88,7 @@
import org.xnio.ByteString;

@SuppressWarnings("MustBeClosedChecker")
@AllowedRangeRequest(justification = "test!")
public abstract class AbstractKeyValueServiceTest {
private final KvsManager kvsManager;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.common.primitives.Ints;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.internalschema.TransactionSchemaManager;
import com.palantir.atlasdb.keyvalue.api.AllowedRangeRequest;
import com.palantir.atlasdb.keyvalue.api.Cell;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.RangeRequest;
Expand Down Expand Up @@ -168,6 +169,7 @@ public void smokeTest() throws Exception {

protected abstract KeyValueService getKeyValueService();

@AllowedRangeRequest(justification = "test")
void verifyTableSwept(TableReference tableRef, int expectedCells, boolean conservative) {
try (ClosableIterator<RowResult<Set<Long>>> iter =
kvs.getRangeOfTimestamps(tableRef, RangeRequest.all(), Long.MAX_VALUE)) {
Expand Down