public List<RowLogMessage> next(String subscription, Long minimalTimestamp, boolean problematic) throws RowLogException { byte[] rowPrefix; byte[] subscriptionBytes = Bytes.toBytes(subscription); if (problematic) { rowPrefix = PROBLEMATIC_MARKER; rowPrefix = Bytes.add(rowPrefix, subscriptionBytes); } else { rowPrefix = subscriptionBytes; } byte[] startRow = rowPrefix; if (minimalTimestamp != null) startRow = Bytes.add(startRow, Bytes.toBytes(minimalTimestamp)); try { List<RowLogMessage> rowLogMessages = new ArrayList<RowLogMessage>(); Scan scan = new Scan(startRow); if (minimalTimestamp != null) scan.setTimeRange(minimalTimestamp, Long.MAX_VALUE); scan.addColumn(MESSAGES_CF, MESSAGE_COLUMN); ResultScanner scanner = table.getScanner(scan); boolean keepScanning = problematic; do { Result[] results = scanner.next(batchSize); if (results.length == 0) { keepScanning = false; } for (Result next : results) { byte[] rowKey = next.getRow(); if (!Bytes.startsWith(rowKey, rowPrefix)) { keepScanning = false; break; // There were no messages for this subscription } if (problematic) { rowKey = Bytes.tail(rowKey, rowKey.length - PROBLEMATIC_MARKER.length); } byte[] value = next.getValue(MESSAGES_CF, MESSAGE_COLUMN); byte[] messageId = Bytes.tail(rowKey, rowKey.length - subscriptionBytes.length); rowLogMessages.add(decodeMessage(messageId, value)); } } while (keepScanning); // The scanner is not closed in a finally block, since when we get an IOException from // HBase, it is likely that closing the scanner will give problems too. Not closing // the scanner is not fatal since HBase will expire it after a while. Closer.close(scanner); return rowLogMessages; } catch (IOException e) { throw new RowLogException("Failed to fetch next message from RowLogShard", e); } }
@Override /** * Checks if a blob key (ourStoreKey) is contained in the blob field (theirValue). The blob field * can be a multivalue and / or hierarchical field. * * <p>IMPORTANT: This implementation depends on the byte encodings from ValueTypeImpl, * BlobValueType and DataOutputImpl. Any changes there have an impact on this implementation. */ public int compareTo(byte[] theirValue, int fromOffset, int length) { byte[] ourStoreKey = Bytes.tail(nestingLevelAndValue, nestingLevelAndValue.length - Bytes.SIZEOF_INT); if (theirValue == null && ourStoreKey == null) return 0; if (length == 0 && ourStoreKey.length == 0) return 0; if (length < ourStoreKey.length) return -1; if (theirValue[fromOffset] == (byte) (1)) { // First byte indicates if it was deleted or not return -1; } int nestingLevel = Bytes.toInt(nestingLevelAndValue); offset = fromOffset + 1; return compareBlob(nestingLevel, ourStoreKey, theirValue); }
private RowLogMessage decodeMessage(byte[] messageId, byte[] data) { long timestamp = Bytes.toLong(messageId); long seqNr = Bytes.toLong(messageId, Bytes.SIZEOF_LONG); byte[] rowKey = Bytes.tail(messageId, messageId.length - (2 * Bytes.SIZEOF_LONG)); return new RowLogMessageImpl(timestamp, rowKey, seqNr, data, rowLog); }