public Position(int ts, int order, Long opId) { this(new BsonTimestamp(ts, order), opId); }
@Override protected BsonTimestamp doReadTimestamp() { return new BsonTimestamp(bsonInput.readInt64()); }
private static <T> Object doGetTimestamp(Object timestamp, Class<T> targetType) { if (ClassUtils.isAssignableValue(targetType, timestamp)) { return timestamp; } if (timestamp instanceof Instant) { return new BsonTimestamp((int) ((Instant) timestamp).getEpochSecond(), 0); } if (timestamp instanceof BsonTimestamp) { return Instant.ofEpochSecond(((BsonTimestamp) timestamp).getTime()); } throw new IllegalArgumentException( "o_O that should actually not happen. The timestamp should be an Instant or a BsonTimestamp but was " + ObjectUtils.nullSafeClassName(timestamp)); }
@Override public void encode(final BsonWriter writer, final BSONTimestamp value, final EncoderContext encoderContext) { writer.writeTimestamp(new BsonTimestamp(value.getTime(), value.getInc())); }
/** * Encodes a BSON timestamp * * @param name the field name * @param timestamp the timestamp to encode * @see org.bson.BsonType#TIMESTAMP */ protected void putTimestamp(final String name, final BSONTimestamp timestamp) { putName(name); bsonWriter.writeTimestamp(new BsonTimestamp(timestamp.getTime(), timestamp.getInc())); }
private Document createEvent(Document obj, String op) { BsonTimestamp ts = new BsonTimestamp(1000, 1); return new Document() .append("o", obj) .append("ns", "dbA.c1") .append("ts", ts) .append("h", 12345678L) .append("op", op); }
private Document createEvent(Document obj, String op) { BsonTimestamp ts = new BsonTimestamp(1000, 1); return new Document() .append("o", obj) .append("ns", "dbA.c1") .append("ts", ts) .append("h", 12345678L) .append("op", op); }
private Document createUpdateEvent(Document obj, ObjectId objId) { BsonTimestamp ts = new BsonTimestamp(1000, 1); return new Document() .append("o", obj) .append("o2", objId) .append("ns", "dbA.c1") .append("ts", ts) .append("h", 12345678L) .append("op", "u"); } }
private Document createUpdateEvent(Document obj, ObjectId objId) { BsonTimestamp ts = new BsonTimestamp(1000, 1); return new Document() .append("o", obj) .append("o2", objId) .append("ns", "dbA.c1") .append("ts", ts) .append("h", 12345678L) .append("op", "u"); } }
@Test @FixFor("DBZ-583") public void shouldDropDeleteMessagesByDefault() throws InterruptedException { RecordMakers recordMakers = new RecordMakers(filters, source, topicSelector, produced::add, false); BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document("_id", objId); // given Document event = new Document().append("o", obj) .append("ns", "dbA.c1") .append("ts", ts) .append("h", Long.valueOf(12345678)) .append("op", "d"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); // when SourceRecord transformed = transformation.apply(record); // then assert transformed message is skipped assertThat(transformed).isNull(); }
@Test @FixFor("DBZ-971") public void shouldPropagatePreviousRecordHeaders() throws InterruptedException { BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document().append("$set", new Document("name", "Sally")); // given Document event = new Document().append("o", obj) .append("o2", objId) .append("ns", "dbA.c1") .append("ts", ts) .append("h", Long.valueOf(12345678)) .append("op", "u"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); record.headers().addString("application/debezium-test-header", "shouldPropagatePreviousRecordHeaders"); // when SourceRecord transformedRecord = transformation.apply(record); assertThat(transformedRecord.headers()).hasSize(1); Iterator<Header> headers = transformedRecord.headers().allWithName("application/debezium-test-header"); assertThat(headers.hasNext()).isTrue(); assertThat(headers.next().value().toString()).isEqualTo("shouldPropagatePreviousRecordHeaders"); }
private BsonTimestamp visitTimestampConstructor() { verifyToken(JsonTokenType.LEFT_PAREN); JsonToken timeToken = popToken(); int time; if (timeToken.getType() != JsonTokenType.INT32) { throw new JsonParseException("JSON reader expected an integer but found '%s'.", timeToken.getValue()); } else { time = timeToken.getValue(Integer.class); } verifyToken(JsonTokenType.COMMA); JsonToken incrementToken = popToken(); int increment; if (incrementToken.getType() != JsonTokenType.INT32) { throw new JsonParseException("JSON reader expected an integer but found '%s'.", timeToken.getValue()); } else { increment = incrementToken.getValue(Integer.class); } verifyToken(JsonTokenType.RIGHT_PAREN); return new BsonTimestamp(time, increment); }
@Test @FixFor("DBZ-583") public void shouldRewriteMessagesWhichAreNotDeletes() throws InterruptedException { BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document().append("$set", new Document("name", "Sally")); // given Document event = new Document().append("o", obj) .append("o2", objId) .append("ns", "dbA.c1") .append("ts", ts) .append("h", Long.valueOf(12345678)) .append("op", "u"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); final Map<String, String> props = new HashMap<>(); props.put(HANDLE_DELETES, "rewrite"); transformation.configure(props); // when SourceRecord transformedRecord = transformation.apply(record); Struct value = (Struct) transformedRecord.value(); // then assert value and its schema assertThat(value.schema().field("__deleted").schema()).isEqualTo(SchemaBuilder.OPTIONAL_BOOLEAN_SCHEMA); assertThat(value.get("__deleted")).isEqualTo(false); }
@Test @FixFor("DBZ-612") public void shouldGenerateRecordForUpdateEventWithUnset() throws InterruptedException { BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId();
@Test @FixFor("DBZ-612") public void shouldGenerateRecordForUnsetOnlyUpdateEvent() throws InterruptedException { BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document() .append("$unset", new Document().append("phone", true).append("active", false)) ; // given Document event = new Document().append("o", obj) .append("o2", objId) .append("ns", "dbA.c1") .append("ts", ts) .append("h", Long.valueOf(12345678)) .append("op", "u"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); // when SourceRecord transformed = transformation.apply(record); Struct value = (Struct) transformed.value(); // and then assert value and its schema assertThat(value.schema()).isSameAs(transformed.valueSchema()); assertThat(value.get("phone")).isEqualTo(null); assertThat(value.schema().field("phone").schema()).isEqualTo(SchemaBuilder.OPTIONAL_STRING_SCHEMA); assertThat(value.schema().fields()).hasSize(2); }
RecordMakers recordMakers = new RecordMakers(filters, source, topicSelector, produced::add, false); BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId();
@Test public void shouldGenerateRecordForInsertEvent() throws InterruptedException { CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); BsonTimestamp ts = new BsonTimestamp(1000, 1); ObjectId objId = new ObjectId(); Document obj = new Document().append("_id", objId).append("name", "Sally"); Document event = new Document().append("o", obj) .append("ns", "dbA.c1") .append("ts", ts) .append("h", Long.valueOf(12345678)) .append("op", "i"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); Struct key = (Struct) record.key(); Struct value = (Struct) record.value(); assertThat(key.schema()).isSameAs(record.keySchema()); assertThat(key.get("id")).isEqualTo("{ \"$oid\" : \"" + objId + "\"}"); assertThat(value.schema()).isSameAs(record.valueSchema()); // assertThat(value.getString(FieldName.BEFORE)).isNull(); assertThat(value.getString(FieldName.AFTER)).isEqualTo(obj.toJson(WRITER_SETTINGS)); assertThat(value.getString(FieldName.OPERATION)).isEqualTo(Operation.CREATE.code()); assertThat(value.getInt64(FieldName.TIMESTAMP)).isEqualTo(1002L); Struct actualSource = value.getStruct(FieldName.SOURCE); Struct expectedSource = source.lastOffsetStruct("rs0", collectionId); assertThat(actualSource).isEqualTo(expectedSource); }
@Test public void shouldReturnRecordedOffsetForUsedReplicaName() { Document event = new Document().append("ts", new BsonTimestamp(100, 2)) .append("h", Long.valueOf(1987654321)) .append("ns", "dbA.collectA"); assertThat(source.hasOffset(REPLICA_SET_NAME)).isEqualTo(false); source.offsetStructForEvent(REPLICA_SET_NAME, event); assertThat(source.hasOffset(REPLICA_SET_NAME)).isEqualTo(true); Map<String, ?> offset = source.lastOffset(REPLICA_SET_NAME); assertThat(offset.get(SourceInfo.TIMESTAMP)).isEqualTo(100); assertThat(offset.get(SourceInfo.ORDER)).isEqualTo(2); assertThat(offset.get(SourceInfo.OPERATION_ID)).isEqualTo(1987654321L); BsonTimestamp ts = source.lastOffsetTimestamp(REPLICA_SET_NAME); assertThat(ts.getTime()).isEqualTo(100); assertThat(ts.getInc()).isEqualTo(2); Struct struct = source.lastOffsetStruct(REPLICA_SET_NAME,new CollectionId(REPLICA_SET_NAME,"dbA","collectA")); assertThat(struct.getInt32(SourceInfo.TIMESTAMP)).isEqualTo(100); assertThat(struct.getInt32(SourceInfo.ORDER)).isEqualTo(2); assertThat(struct.getInt64(SourceInfo.OPERATION_ID)).isEqualTo(1987654321L); assertThat(struct.getString(SourceInfo.NAMESPACE)).isEqualTo("dbA.collectA"); assertThat(struct.getString(SourceInfo.REPLICA_SET_NAME)).isEqualTo(REPLICA_SET_NAME); assertThat(struct.getString(SourceInfo.SERVER_NAME)).isEqualTo("serverX"); assertThat(struct.getBoolean(SourceInfo.INITIAL_SYNC)).isNull(); }
@Test public void shouldReturnRecordedOffsetForUsedReplicaNameDuringInitialSync() { source.startInitialSync(REPLICA_SET_NAME); Document event = new Document().append("ts", new BsonTimestamp(100, 2)) .append("h", Long.valueOf(1987654321)) .append("ns", "dbA.collectA"); assertThat(source.hasOffset(REPLICA_SET_NAME)).isEqualTo(false); source.offsetStructForEvent(REPLICA_SET_NAME, event); assertThat(source.hasOffset(REPLICA_SET_NAME)).isEqualTo(true); Map<String, ?> offset = source.lastOffset(REPLICA_SET_NAME); assertThat(offset.get(SourceInfo.TIMESTAMP)).isEqualTo(100); assertThat(offset.get(SourceInfo.ORDER)).isEqualTo(2); assertThat(offset.get(SourceInfo.OPERATION_ID)).isEqualTo(1987654321L); BsonTimestamp ts = source.lastOffsetTimestamp(REPLICA_SET_NAME); assertThat(ts.getTime()).isEqualTo(100); assertThat(ts.getInc()).isEqualTo(2); Struct struct = source.lastOffsetStruct(REPLICA_SET_NAME,new CollectionId(REPLICA_SET_NAME,"dbA","collectA")); assertThat(struct.getInt32(SourceInfo.TIMESTAMP)).isEqualTo(100); assertThat(struct.getInt32(SourceInfo.ORDER)).isEqualTo(2); assertThat(struct.getInt64(SourceInfo.OPERATION_ID)).isEqualTo(1987654321L); assertThat(struct.getString(SourceInfo.NAMESPACE)).isEqualTo("dbA.collectA"); assertThat(struct.getString(SourceInfo.REPLICA_SET_NAME)).isEqualTo(REPLICA_SET_NAME); assertThat(struct.getString(SourceInfo.SERVER_NAME)).isEqualTo("serverX"); assertThat(struct.getBoolean(SourceInfo.INITIAL_SYNC)).isEqualTo(true); }
@Test @FixFor("DBZ-582") public void shouldGenerateRecordForDeleteEventWithoutTombstone() throws InterruptedException { RecordMakers recordMakers = new RecordMakers(filters, source, topicSelector, produced::add, false); BsonTimestamp ts = new BsonTimestamp(1000, 1); CollectionId collectionId = new CollectionId("rs0", "dbA", "c1"); ObjectId objId = new ObjectId(); Document obj = new Document("_id", objId); Document event = new Document().append("o", obj) .append("ns", "dbA.c1") .append("ts", ts) .append("h", new Long(12345678)) .append("op", "d"); RecordsForCollection records = recordMakers.forCollection(collectionId); records.recordEvent(event, 1002); assertThat(produced.size()).isEqualTo(1); SourceRecord record = produced.get(0); Struct key = (Struct) record.key(); Struct value = (Struct) record.value(); assertThat(key.schema()).isSameAs(record.keySchema()); assertThat(key.get("id")).isEqualTo(JSONSerializers.getStrict().serialize(objId)); assertThat(value.schema()).isSameAs(record.valueSchema()); assertThat(value.getString(FieldName.AFTER)).isNull(); assertThat(value.getString("patch")).isNull(); assertThat(value.getString(FieldName.OPERATION)).isEqualTo(Operation.DELETE.code()); assertThat(value.getInt64(FieldName.TIMESTAMP)).isEqualTo(1002L); Struct actualSource = value.getStruct(FieldName.SOURCE); Struct expectedSource = source.lastOffsetStruct("rs0", collectionId); assertThat(actualSource).isEqualTo(expectedSource); }