public static CqlQuery constructCheckAndSetMultipleQuery(Map<String, Pair<byte[], byte[]>> checkAndSetRequest) { StringBuilder builder = new StringBuilder(); builder.append("BEGIN UNLOGGED BATCH\n"); // Safe, because all updates are on the same partition key // Safe, because ordering does not apply in batches checkAndSetRequest.forEach((columnName, value) -> { byte[] expected = value.getLhSide(); byte[] target = value.getRhSide(); builder.append(constructCheckAndSetQuery(columnName, expected, target)); }); builder.append("APPLY BATCH;"); // This looks awkward. However, we know that all expressions in this String pertain to timestamps and known // table references, hence this is actually safe. Doing this quickly owing to priority. // TODO (jkong): Build up a query by passing around legitimate formats and args. return CqlQuery.builder() .safeQueryFormat(builder.toString()) .build(); }
public void logSlowResult(KvsProfilingLogger.LoggingFunction log, Stopwatch timer) { Object[] allArgs = new Object[args().size() + 3]; allArgs[0] = SafeArg.of("queryFormat", safeQueryFormat()); allArgs[1] = UnsafeArg.of("fullQuery", toString()); allArgs[2] = LoggingArgs.durationMillis(timer); System.arraycopy(args().toArray(), 0, allArgs, 3, args().size()); log.log("A CQL query was slow: queryFormat = [{}], fullQuery = [{}], durationMillis = {}", allArgs); }
@Override public CqlResult execute_cql3_query(CqlQuery cqlQuery, Compression compression, ConsistencyLevel consistency) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException, TException { try (CloseableTrace trace = startLocalTrace("cqlExecutor.execute_cql3_query(query {})", cqlQuery.getLazySafeLoggableObject())) { return client.execute_cql3_query(cqlQuery, compression, consistency); } }
@Override public String toString() { return String.format(safeQueryFormat(), args().toArray()); }
@Override public String toString() { return cqlQuery.toString(); } };
@Test public void valuesCreatedAtCorrectLogSafetyLevelsForNewCells() { CqlQuery query = CheckAndSetQueries.getQueryForRequest(NEW_CELL_REQUEST); AtomicReference<Object[]> objects = new AtomicReference<>(); query.logSlowResult((format, args) -> objects.set(args), Stopwatch.createStarted()); Object[] loggedObjects = objects.get(); Map<String, Boolean> argumentSafety = Maps.newHashMap(); Arrays.stream(loggedObjects) .forEach(object -> { Arg<?> arg = (Arg) object; argumentSafety.put(arg.getName(), arg.isSafeForLogging()); }); assertThat(argumentSafety) .containsEntry("row", false) .containsEntry("column", false) .containsEntry("cassandraTimestamp", true) .containsEntry("newValue", false) .containsEntry("unsafeTableRef", false) .doesNotContainKey("tableRef"); // the table wasn't marked as safe assertThat(query.toString()) .isEqualTo("INSERT INTO \"ns__table\" (key, column1, column2, value)" + " VALUES (0x616263, 0x313233, -1, 0x70747074) IF NOT EXISTS;"); }
@Override public CqlResult execute_cql3_query(CqlQuery cqlQuery, Compression compression, ConsistencyLevel consistency) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException, TException { long startTime = System.currentTimeMillis(); return KvsProfilingLogger.maybeLog( (KvsProfilingLogger.CallableCheckedException<CqlResult, TException>) () -> client.execute_cql3_query(cqlQuery, compression, consistency), (logger, timer) -> cqlQuery.logSlowResult(logger, timer), (logger, cqlResult) -> { if (cqlResult.getRows() == null) { // different from an empty list logger.log("and returned null rows. The query was started at time {}", LoggingArgs.startTimeMillis(startTime)); } else { logger.log("and returned {} rows. The query was started at time {}", SafeArg.of("numRows", cqlResult.getRows().size()), LoggingArgs.startTimeMillis(startTime)); } }); } }
@Override public CqlResult execute_cql3_query(CqlQuery cqlQuery, Compression compression, ConsistencyLevel consistency) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException, TException { ByteBuffer queryBytes = ByteBuffer.wrap(cqlQuery.toString().getBytes(StandardCharsets.UTF_8)); return executeHandlingExceptions(() -> client.execute_cql3_query(queryBytes, compression, consistency)); }
@Test public void valuesCreatedAtCorrectLogSafetyLevelsForUpdates() { CqlQuery query = CheckAndSetQueries.getQueryForRequest(UPDATE_REQUEST); AtomicReference<Object[]> objects = new AtomicReference<>(); query.logSlowResult((format, args) -> objects.set(args), Stopwatch.createStarted()); Object[] loggedObjects = objects.get(); Map<String, Boolean> argumentSafety = Maps.newHashMap(); Arrays.stream(loggedObjects) .forEach(object -> { Arg<?> arg = (Arg) object; argumentSafety.put(arg.getName(), arg.isSafeForLogging()); }); assertThat(argumentSafety) .containsEntry("row", false) .containsEntry("column", false) .containsEntry("cassandraTimestamp", true) .containsEntry("oldValue", false) .containsEntry("newValue", false) .containsEntry("unsafeTableRef", false) .doesNotContainKey("tableRef"); // the table wasn't marked as safe assertThat(query.toString()) .isEqualTo("UPDATE \"ns__table\" SET value=0x626262" + " WHERE key=0x616263 AND column1=0x313233 AND column2=-1 IF value=0x616161;"); } }
@Override public String toString() { String argsString = args().stream() .filter(Arg::isSafeForLogging) .map(arg -> String.format("%s = %s", arg.getName(), arg.getValue())) .collect(Collectors.joining(", ")); return safeQueryFormat() + ": " + argsString; } };
@Override public CqlResult execute_cql3_query(CqlQuery cqlQuery, Compression compression, ConsistencyLevel consistency) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException, TException { long startTime = System.currentTimeMillis(); return KvsProfilingLogger.maybeLog( (KvsProfilingLogger.CallableCheckedException<CqlResult, TException>) () -> client.execute_cql3_query(cqlQuery, compression, consistency), (logger, timer) -> cqlQuery.logSlowResult(logger, timer), (logger, cqlResult) -> { if (cqlResult.getRows() == null) { // different from an empty list logger.log("and returned null rows. The query was started at time {}", LoggingArgs.startTimeMillis(startTime)); } else { logger.log("and returned {} rows. The query was started at time {}", SafeArg.of("numRows", cqlResult.getRows().size()), LoggingArgs.startTimeMillis(startTime)); } }); } }
public void logSlowResult(KvsProfilingLogger.LoggingFunction log, Stopwatch timer) { Object[] allArgs = new Object[args().size() + 3]; allArgs[0] = SafeArg.of("queryFormat", safeQueryFormat()); allArgs[1] = UnsafeArg.of("fullQuery", toString()); allArgs[2] = LoggingArgs.durationMillis(timer); System.arraycopy(args().toArray(), 0, allArgs, 3, args().size()); log.log("A CQL query was slow: queryFormat = [{}], fullQuery = [{}], durationMillis = {}", allArgs); }
private ArgumentMatcher<CqlQuery> cqlQueryMatcher(String expected) { return argument -> { if (argument == null) { return false; } return expected.equals(argument.toString()); }; }
@Override public String toString() { return String.format(safeQueryFormat(), args().toArray()); }
/** * Returns a list of {@link CellWithTimestamp}s within the given {@code row}, starting at the (column, timestamp) * pair represented by ({@code startColumnInclusive}, {@code startTimestampExclusive}). */ @Override public List<CellWithTimestamp> getTimestampsWithinRow( TableReference tableRef, byte[] row, byte[] startColumnInclusive, long startTimestampExclusive, int limit) { long invertedTimestamp = ~startTimestampExclusive; String selQuery = "SELECT column1, column2 FROM %s WHERE key = %s AND (column1, column2) > (%s, %s) LIMIT %s;"; CqlQuery query = CqlQuery.builder() .safeQueryFormat(selQuery) .addArgs( quotedTableName(tableRef), key(row), column1(startColumnInclusive), column2(invertedTimestamp), limit(limit)) .build(); return executeAndGetCells(query, row, result -> CqlExecutorImpl.getCellFromKeylessRow(result, row)); }
@Override public CqlResult execute_cql3_query(CqlQuery cqlQuery, Compression compression, ConsistencyLevel consistency) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException, TException { try (CloseableTrace trace = startLocalTrace("cqlExecutor.execute_cql3_query(query {})", cqlQuery.getLazySafeLoggableObject())) { return client.execute_cql3_query(cqlQuery, compression, consistency); } }
@Test public void canGetSelectQuery() { CqlQuery query = CassandraTimestampUtils.constructSelectFromTimestampTableQuery(); assertThat(query.toString()).isEqualTo("SELECT column1, value FROM \"_timestamp\" WHERE key=0x7473;"); }
@Override public String toString() { String argsString = args().stream() .filter(Arg::isSafeForLogging) .map(arg -> String.format("%s = %s", arg.getName(), arg.getValue())) .collect(Collectors.joining(", ")); return safeQueryFormat() + ": " + argsString; } };
public static CqlQuery constructCheckAndSetMultipleQuery(Map<String, Pair<byte[], byte[]>> checkAndSetRequest) { StringBuilder builder = new StringBuilder(); builder.append("BEGIN UNLOGGED BATCH\n"); // Safe, because all updates are on the same partition key // Safe, because ordering does not apply in batches checkAndSetRequest.forEach((columnName, value) -> { byte[] expected = value.getLhSide(); byte[] target = value.getRhSide(); builder.append(constructCheckAndSetQuery(columnName, expected, target)); }); builder.append("APPLY BATCH;"); // This looks awkward. However, we know that all expressions in this String pertain to timestamps and known // table references, hence this is actually safe. Doing this quickly owing to priority. // TODO (jkong): Build up a query by passing around legitimate formats and args. return CqlQuery.builder() .safeQueryFormat(builder.toString()) .build(); }
@Test public void checkAndSetIsUpdateIfEqualIfExpectedIsNotNull() { CqlQuery query = CassandraTimestampUtils.constructCheckAndSetMultipleQuery( ImmutableMap.of(COLUMN_NAME_1, Pair.create(VALUE_1, VALUE_2))); assertThat(query.toString()).contains("UPDATE").contains("IF " + CassandraTimestampUtils.VALUE_COLUMN + "="); }