All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nosqlbench.activitytype.cql.statements.rowoperators.verification.RowDifferencer Maven / Gradle / Ivy

package io.nosqlbench.activitytype.cql.statements.rowoperators.verification;

/*
 * Copyright (c) 2022 nosqlbench
 * 
 * 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.
 */


import com.datastax.driver.core.*;
import io.nosqlbench.activitytype.cql.api.RowCycleOperator;
import io.nosqlbench.activitytype.cql.errorhandling.exceptions.RowVerificationException;
import io.nosqlbench.virtdata.core.bindings.Bindings;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 

RowDifferencer uses the metadata associated with a row to access and compare * {@link Row} values in a type-specific way. *

*/ public class RowDifferencer implements RowCycleOperator { private final StringBuilder logbuffer = new StringBuilder(); private final Map refMap = new HashMap<>(); private final DiffType difftype; private final Bindings bindings; private final VerificationMetrics metrics; private RowDifferencer(VerificationMetrics metrics, Bindings bindings, DiffType diffType) { this.metrics = metrics; this.bindings = bindings; this.difftype = diffType; } /** * see {@link DataType} * * @param typeName The DataType.Name of the field in question * @param row The row to read the field value from * @param fieldName The field name to read * @param genValue the generated value to compare against * @return true, if the value is equal */ private static boolean isEqual(DataType.Name typeName, Row row, String fieldName, Object genValue) { switch (typeName) { case ASCII: // ASCII(1, String.class) case VARCHAR: // VARCHAR(13, String.class) case TEXT: // TEXT(10, String.class) String textValue = row.getString(fieldName); return textValue.equals(genValue); case BIGINT: // BIGINT(2, Long.class) case COUNTER: // COUNTER(5, Long.class) long longValue = row.getLong(fieldName); return longValue == (long) genValue; case BLOB: // BLOB(3, ByteBuffer.class) // TODO: How do we test this one? case CUSTOM: // CUSTOM(0, ByteBuffer.class) ByteBuffer blobValue = row.getBytes(fieldName); return blobValue.equals(genValue); case BOOLEAN: // BOOLEAN(4, Boolean.class) boolean boolValue = row.getBool(fieldName); return boolValue == (boolean) genValue; case DECIMAL: // DECIMAL(6, BigDecimal.class) BigDecimal bigDecimalValue = row.getDecimal(fieldName); return bigDecimalValue.equals(genValue); case DOUBLE: // DOUBLE(7, Double.class) double doubleValue = row.getDouble(fieldName); return doubleValue == (double) genValue; case FLOAT: // FLOAT(8, Float.class) float floatValue = row.getFloat(fieldName); return floatValue == (float) genValue; case INET: // INET(16, InetAddress.class) InetAddress inetAddressValue = row.getInet(fieldName); return inetAddressValue.equals(genValue); case INT: // INT(9, Integer.class) int intValue = row.getInt(fieldName); return intValue == (int) genValue; case TIMESTAMP: // TIMESTAMP(11, Date.class) Date timestamp = row.getTimestamp(fieldName); return timestamp.equals(genValue); case UUID: // UUID(12, UUID.class) case TIMEUUID: // TIMEUUID(15, UUID.class) UUID uuidValue = row.getUUID(fieldName); return uuidValue.equals(genValue); case VARINT: // VARINT(14, BigInteger.class) BigInteger bigIntValue = row.getVarint(fieldName); return bigIntValue.equals(genValue); case LIST: // LIST(32, List.class) // TODO: How do we make getCollection methods work with non-String CQL types? List list = row.getList(fieldName, String.class); return list.equals(genValue); case SET: // SET(34, Set.class) Set set = row.getSet(fieldName, String.class); return set.equals(genValue); case MAP: // MAP(33, Map.class) Map map = row.getMap(fieldName, String.class, String.class); return map.equals(genValue); case UDT: // UDT(48, UDTValue.class) UDTValue udtValue = row.getUDTValue(fieldName); return udtValue.equals(genValue); case TUPLE: // TUPLE(49, TupleValue.class) TupleValue tupleValue = row.getTupleValue(fieldName); return tupleValue.equals(genValue); case SMALLINT: short shortVal = row.getShort(fieldName); return shortVal == (Short) genValue; case TINYINT: byte byteValue = row.getByte(fieldName); return byteValue == (byte) genValue; case DATE: LocalDate dateValue = row.getDate(fieldName); return dateValue.equals(genValue); case TIME: long timeValue = row.getTime(fieldName); return timeValue == (long) genValue; default: throw new RuntimeException("Unrecognized type:" + typeName); } } private static String prettyPrint(DataType.Name typeName, Row row, String fieldName) { switch (typeName) { case ASCII: // ASCII(1, String.class) case VARCHAR: // VARCHAR(13, String.class) case TEXT: // TEXT(10, String.class) return row.getString(fieldName); case BIGINT: // BIGINT(2, Long.class) case COUNTER: // COUNTER(5, Long.class) long counterValue = row.getLong(fieldName); return String.valueOf(counterValue); case BLOB: // BLOB(3, ByteBuffer.class) case CUSTOM: // CUSTOM(0, ByteBuffer.class) ByteBuffer blobValue = row.getBytes(fieldName); return String.valueOf(blobValue); case BOOLEAN: // BOOLEAN(4, Boolean.class) boolean boolValue = row.getBool(fieldName); return String.valueOf(boolValue); case DECIMAL: // DECIMAL(6, BigDecimal.class) BigDecimal bigDecimalValue = row.getDecimal(fieldName); return String.valueOf(bigDecimalValue); case DOUBLE: // DOUBLE(7, Double.class) double doubleValue = row.getDouble(fieldName); return String.valueOf(doubleValue); case FLOAT: // FLOAT(8, Float.class) float floatValue = row.getFloat(fieldName); return String.valueOf(floatValue); case INET: // INET(16, InetAddress.class) InetAddress inetAddressValue = row.getInet(fieldName); return String.valueOf(inetAddressValue); case INT: // INT(9, Integer.class) int intValue = row.getInt(fieldName); return String.valueOf(intValue); case TIMESTAMP: // TIMESTAMP(11, Date.class) Date timestamp = row.getTimestamp(fieldName); return String.valueOf(timestamp); case UUID: // UUID(12, UUID.class) case TIMEUUID: // TIMEUUID(15, UUID.class) UUID uuidValue = row.getUUID(fieldName); return String.valueOf(uuidValue); case VARINT: // VARINT(14, BigInteger.class) BigInteger bigIntValue = row.getVarint(fieldName); return String.valueOf(bigIntValue); case LIST: // LIST(32, List.class) List list = row.getList(fieldName, String.class); return String.valueOf(list); case SET: // SET(34, Set.class) Set set = row.getSet(fieldName, String.class); return String.valueOf(set); case MAP: // MAP(33, Map.class) Map map = row.getMap(fieldName, String.class, String.class); return String.valueOf(map); case UDT: // UDT(48, UDTValue.class) UDTValue udtValue = row.getUDTValue(fieldName); return String.valueOf(udtValue); case TUPLE: // TUPLE(49, TupleValue.class) TupleValue tupleValue = row.getTupleValue(fieldName); return String.valueOf(tupleValue); case SMALLINT: short val = row.getShort(fieldName); return String.valueOf(val); case TINYINT: byte byteValue = row.getByte(fieldName); return String.valueOf(byteValue); case DATE: LocalDate dateValue = row.getDate(fieldName); return String.valueOf(dateValue); case TIME: long timeValue = row.getTime(fieldName); return String.valueOf(timeValue); default: throw new RuntimeException("Type not recognized:" + typeName); } } /** * Compare the values of the row with the values generated. *

* Specifically, *

    *
  1. Ensure the same number of fields.
  2. *
  3. Ensure the same pair-wise field names.
  4. *
  5. Ensure that each pair of same-named fields has the same data type.
  6. *
  7. Ensure that the value of each pair of fields is equal according to the equals * operator for the respective type.
  8. *
* * * * @param row A row of data * @param referenceMap a map of values * @return a count of differences between the row and the reference values */ private int compare(Row row, Map referenceMap) { int diff = 0; ColumnDefinitions cdefs = row.getColumnDefinitions(); logbuffer.setLength(0); if (difftype.is(DiffType.reffields)) { List missingRowFields = referenceMap.keySet().stream() .filter(gk -> !cdefs.contains(gk)) .collect(Collectors.toList()); if (missingRowFields.size() > 0) { diff += missingRowFields.size(); logbuffer.append("\nexpected fields '"); logbuffer.append(String.join("','", missingRowFields)); logbuffer.append("' not in row."); } } if (difftype.is(DiffType.rowfields)) { List missingRefFields = cdefs.asList().stream() .map(ColumnDefinitions.Definition::getName) .filter(k -> !referenceMap.containsKey(k)) .collect(Collectors.toList()); if (missingRefFields.size() > 0) { diff += missingRefFields.size(); logbuffer.append("\nexpected fields '"); logbuffer.append(String.join("','", missingRefFields)); logbuffer.append("' not in reference data: " + referenceMap); } } if (difftype.is(DiffType.values)) { for (ColumnDefinitions.Definition definition : row.getColumnDefinitions()) { String name = definition.getName(); if (referenceMap.containsKey(name)) { DataType type = definition.getType(); if (!isEqual(type.getName(), row, name, referenceMap.get(name))) { logbuffer.append("\nvalue differs for '").append(name).append("' "); logbuffer.append("expected:'").append(referenceMap.get(name).toString()).append("'"); logbuffer.append(" actual:'").append(prettyPrint(type.getName(), row, name)).append("'"); diff++; metrics.unverifiedValuesCounter.inc(); } else { metrics.verifiedValuesCounter.inc(); } } } } if (diff == 0) { metrics.verifiedRowsCounter.inc(); } else { metrics.unverifiedRowsCounter.inc(); } return diff; } /** * Get the most recent detail log recorded by this thread. * * @return a logbuffer string, with one entry per line */ public String getDetail() { return this.logbuffer.toString(); } @Override public int apply(Row row, long cycle) { refMap.clear(); bindings.setMap(refMap, cycle); int diffs = compare(row, refMap); if (diffs > 0) { HashMap mapcopy = new HashMap<>(); mapcopy.putAll(refMap); throw new RowVerificationException(cycle, row, mapcopy, getDetail()); } else { return 0; } } public static class ThreadLocalWrapper implements RowCycleOperator { private final VerificationMetrics metrics; private final Bindings bindings; private final DiffType diffType; private final ThreadLocal tl; public ThreadLocalWrapper(VerificationMetrics metrics, Bindings bindings, DiffType diffType) { this.metrics = metrics; this.bindings = bindings; this.diffType = diffType; tl = ThreadLocal.withInitial(() -> new RowDifferencer(metrics,bindings,diffType)); } @Override public int apply(Row row, long cycle) { return tl.get().apply(row,cycle); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy