Timestamp in EventEnvelope, #28331 (#28383)

* added to EventEnvolope and therefore include case class stuff
  for binary compatibility
* also added in PersistentRepr, which for example is the serialized format in
  LeveldbJournal
This commit is contained in:
Patrik Nordwall 2020-01-07 11:32:15 +01:00 committed by Arnout Engelen
parent 290a6137e0
commit efa856bc17
12 changed files with 287 additions and 34 deletions

View file

@ -158,6 +158,17 @@ public final class MessageFormats {
*/
akka.protobufv3.internal.ByteString
getWriterUuidBytes();
/**
* <code>optional sint64 timestamp = 14;</code>
* @return Whether the timestamp field is set.
*/
boolean hasTimestamp();
/**
* <code>optional sint64 timestamp = 14;</code>
* @return The timestamp.
*/
long getTimestamp();
}
/**
* Protobuf type {@code PersistentMessage}
@ -256,6 +267,11 @@ public final class MessageFormats {
writerUuid_ = bs;
break;
}
case 112: {
bitField0_ |= 0x00000080;
timestamp_ = input.readSInt64();
break;
}
default: {
if (!parseUnknownField(
input, unknownFields, extensionRegistry, tag)) {
@ -558,6 +574,23 @@ public final class MessageFormats {
}
}
public static final int TIMESTAMP_FIELD_NUMBER = 14;
private long timestamp_;
/**
* <code>optional sint64 timestamp = 14;</code>
* @return Whether the timestamp field is set.
*/
public boolean hasTimestamp() {
return ((bitField0_ & 0x00000080) != 0);
}
/**
* <code>optional sint64 timestamp = 14;</code>
* @return The timestamp.
*/
public long getTimestamp() {
return timestamp_;
}
private byte memoizedIsInitialized = -1;
@java.lang.Override
public final boolean isInitialized() {
@ -599,6 +632,9 @@ public final class MessageFormats {
if (((bitField0_ & 0x00000040) != 0)) {
akka.protobufv3.internal.GeneratedMessageV3.writeString(output, 13, writerUuid_);
}
if (((bitField0_ & 0x00000080) != 0)) {
output.writeSInt64(14, timestamp_);
}
unknownFields.writeTo(output);
}
@ -632,6 +668,10 @@ public final class MessageFormats {
if (((bitField0_ & 0x00000040) != 0)) {
size += akka.protobufv3.internal.GeneratedMessageV3.computeStringSize(13, writerUuid_);
}
if (((bitField0_ & 0x00000080) != 0)) {
size += akka.protobufv3.internal.CodedOutputStream
.computeSInt64Size(14, timestamp_);
}
size += unknownFields.getSerializedSize();
memoizedSize = size;
return size;
@ -682,6 +722,11 @@ public final class MessageFormats {
if (!getWriterUuid()
.equals(other.getWriterUuid())) return false;
}
if (hasTimestamp() != other.hasTimestamp()) return false;
if (hasTimestamp()) {
if (getTimestamp()
!= other.getTimestamp()) return false;
}
if (!unknownFields.equals(other.unknownFields)) return false;
return true;
}
@ -723,6 +768,11 @@ public final class MessageFormats {
hash = (37 * hash) + WRITERUUID_FIELD_NUMBER;
hash = (53 * hash) + getWriterUuid().hashCode();
}
if (hasTimestamp()) {
hash = (37 * hash) + TIMESTAMP_FIELD_NUMBER;
hash = (53 * hash) + akka.protobufv3.internal.Internal.hashLong(
getTimestamp());
}
hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash;
return hash;
@ -875,6 +925,8 @@ public final class MessageFormats {
bitField0_ = (bitField0_ & ~0x00000020);
writerUuid_ = "";
bitField0_ = (bitField0_ & ~0x00000040);
timestamp_ = 0L;
bitField0_ = (bitField0_ & ~0x00000080);
return this;
}
@ -935,6 +987,10 @@ public final class MessageFormats {
to_bitField0_ |= 0x00000040;
}
result.writerUuid_ = writerUuid_;
if (((from_bitField0_ & 0x00000080) != 0)) {
result.timestamp_ = timestamp_;
to_bitField0_ |= 0x00000080;
}
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@ -1013,6 +1069,9 @@ public final class MessageFormats {
writerUuid_ = other.writerUuid_;
onChanged();
}
if (other.hasTimestamp()) {
setTimestamp(other.getTimestamp());
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
return this;
@ -1641,6 +1700,43 @@ public final class MessageFormats {
onChanged();
return this;
}
private long timestamp_ ;
/**
* <code>optional sint64 timestamp = 14;</code>
* @return Whether the timestamp field is set.
*/
public boolean hasTimestamp() {
return ((bitField0_ & 0x00000080) != 0);
}
/**
* <code>optional sint64 timestamp = 14;</code>
* @return The timestamp.
*/
public long getTimestamp() {
return timestamp_;
}
/**
* <code>optional sint64 timestamp = 14;</code>
* @param value The timestamp to set.
* @return This builder for chaining.
*/
public Builder setTimestamp(long value) {
bitField0_ |= 0x00000080;
timestamp_ = value;
onChanged();
return this;
}
/**
* <code>optional sint64 timestamp = 14;</code>
* @return This builder for chaining.
*/
public Builder clearTimestamp() {
bitField0_ = (bitField0_ & ~0x00000080);
timestamp_ = 0L;
onChanged();
return this;
}
@java.lang.Override
public final Builder setUnknownFields(
final akka.protobufv3.internal.UnknownFieldSet unknownFields) {
@ -6963,27 +7059,27 @@ public final class MessageFormats {
descriptor;
static {
java.lang.String[] descriptorData = {
"\n\024MessageFormats.proto\"\252\001\n\021PersistentMes" +
"\n\024MessageFormats.proto\"\275\001\n\021PersistentMes" +
"sage\022#\n\007payload\030\001 \001(\0132\022.PersistentPayloa" +
"d\022\022\n\nsequenceNr\030\002 \001(\003\022\025\n\rpersistenceId\030\003" +
" \001(\t\022\017\n\007deleted\030\004 \001(\010\022\016\n\006sender\030\013 \001(\t\022\020\n" +
"\010manifest\030\014 \001(\t\022\022\n\nwriterUuid\030\r \001(\t\"S\n\021P" +
"ersistentPayload\022\024\n\014serializerId\030\001 \002(\005\022\017" +
"\n\007payload\030\002 \002(\014\022\027\n\017payloadManifest\030\003 \001(\014" +
"\"2\n\013AtomicWrite\022#\n\007payload\030\001 \003(\0132\022.Persi" +
"stentMessage\"\356\001\n\033AtLeastOnceDeliverySnap" +
"shot\022\031\n\021currentDeliveryId\030\001 \002(\003\022O\n\025uncon" +
"firmedDeliveries\030\002 \003(\01320.AtLeastOnceDeli" +
"verySnapshot.UnconfirmedDelivery\032c\n\023Unco" +
"nfirmedDelivery\022\022\n\ndeliveryId\030\001 \002(\003\022\023\n\013d" +
"estination\030\002 \002(\t\022#\n\007payload\030\003 \002(\0132\022.Pers" +
"istentPayload\"\\\n\032PersistentStateChangeEv" +
"ent\022\027\n\017stateIdentifier\030\001 \002(\t\022\017\n\007timeout\030" +
"\002 \001(\t\022\024\n\014timeoutNanos\030\003 \001(\003\"h\n\025Persisten" +
"tFSMSnapshot\022\027\n\017stateIdentifier\030\001 \002(\t\022 \n" +
"\004data\030\002 \002(\0132\022.PersistentPayload\022\024\n\014timeo" +
"utNanos\030\003 \001(\003B\"\n\036akka.persistence.serial" +
"izationH\001"
"\010manifest\030\014 \001(\t\022\022\n\nwriterUuid\030\r \001(\t\022\021\n\tt" +
"imestamp\030\016 \001(\022\"S\n\021PersistentPayload\022\024\n\014s" +
"erializerId\030\001 \002(\005\022\017\n\007payload\030\002 \002(\014\022\027\n\017pa" +
"yloadManifest\030\003 \001(\014\"2\n\013AtomicWrite\022#\n\007pa" +
"yload\030\001 \003(\0132\022.PersistentMessage\"\356\001\n\033AtLe" +
"astOnceDeliverySnapshot\022\031\n\021currentDelive" +
"ryId\030\001 \002(\003\022O\n\025unconfirmedDeliveries\030\002 \003(" +
"\01320.AtLeastOnceDeliverySnapshot.Unconfir" +
"medDelivery\032c\n\023UnconfirmedDelivery\022\022\n\nde" +
"liveryId\030\001 \002(\003\022\023\n\013destination\030\002 \002(\t\022#\n\007p" +
"ayload\030\003 \002(\0132\022.PersistentPayload\"\\\n\032Pers" +
"istentStateChangeEvent\022\027\n\017stateIdentifie" +
"r\030\001 \002(\t\022\017\n\007timeout\030\002 \001(\t\022\024\n\014timeoutNanos" +
"\030\003 \001(\003\"h\n\025PersistentFSMSnapshot\022\027\n\017state" +
"Identifier\030\001 \002(\t\022 \n\004data\030\002 \002(\0132\022.Persist" +
"entPayload\022\024\n\014timeoutNanos\030\003 \001(\003B\"\n\036akka" +
".persistence.serializationH\001"
};
descriptor = akka.protobufv3.internal.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
@ -6994,7 +7090,7 @@ public final class MessageFormats {
internal_static_PersistentMessage_fieldAccessorTable = new
akka.protobufv3.internal.GeneratedMessageV3.FieldAccessorTable(
internal_static_PersistentMessage_descriptor,
new java.lang.String[] { "Payload", "SequenceNr", "PersistenceId", "Deleted", "Sender", "Manifest", "WriterUuid", });
new java.lang.String[] { "Payload", "SequenceNr", "PersistenceId", "Deleted", "Sender", "Manifest", "WriterUuid", "Timestamp", });
internal_static_PersistentPayload_descriptor =
getDescriptor().getMessageTypes().get(1);
internal_static_PersistentPayload_fieldAccessorTable = new

View file

@ -0,0 +1,9 @@
# #28331 Add optional timestamp to PersistentRepr
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.persistence.PersistentRepr.timestamp")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.persistence.PersistentRepr.withTimestamp")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.persistence.PersistentImpl.apply")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.persistence.PersistentImpl.copy")
ProblemFilters.exclude[DirectMissingMethodProblem]("akka.persistence.PersistentImpl.this")
ProblemFilters.exclude[Problem]("akka.persistence.PersistentImpl*")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.persistence.serialization.MessageFormats#PersistentMessageOrBuilder.hasTimestamp")
ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.persistence.serialization.MessageFormats#PersistentMessageOrBuilder.getTimestamp")

View file

@ -20,6 +20,7 @@ message PersistentMessage {
optional string sender = 11; // not stored in journal, needed for remote serialization
optional string manifest = 12;
optional string writerUuid = 13;
optional sint64 timestamp = 14;
}
message PersistentPayload {

View file

@ -9,6 +9,7 @@ import akka.persistence.serialization.Message
import scala.collection.immutable
import akka.annotation.DoNotInherit
import akka.util.HashCode
/**
* INTERNAL API
@ -91,6 +92,16 @@ final case class AtomicWrite(payload: immutable.Seq[PersistentRepr]) extends Per
*/
def sequenceNr: Long
/**
* The `timestamp` is the time the event was stored, in milliseconds since midnight, January 1, 1970 UTC
* (same as `System.currentTimeMillis`).
*
* Value `0` is used if undefined.
*/
def timestamp: Long
def withTimestamp(newTimestamp: Long): PersistentRepr
/**
* Unique identifier of the writing persistent actor.
* Used to detect anomalies with overlapping writes from multiple
@ -152,7 +163,7 @@ object PersistentRepr {
deleted: Boolean = false,
sender: ActorRef = null,
writerUuid: String = PersistentRepr.Undefined): PersistentRepr =
PersistentImpl(payload, sequenceNr, persistenceId, manifest, deleted, sender, writerUuid)
PersistentImpl(payload, sequenceNr, persistenceId, manifest, deleted, sender, writerUuid, 0L)
/**
* Java API, Plugin API.
@ -176,7 +187,8 @@ private[persistence] final case class PersistentImpl(
override val manifest: String,
override val deleted: Boolean,
override val sender: ActorRef,
override val writerUuid: String)
override val writerUuid: String,
override val timestamp: Long)
extends PersistentRepr
with NoSerializationVerificationNeeded {
@ -187,6 +199,10 @@ private[persistence] final case class PersistentImpl(
if (this.manifest == manifest) this
else copy(manifest = manifest)
override def withTimestamp(newTimestamp: Long): PersistentRepr =
if (this.timestamp == newTimestamp) this
else copy(timestamp = newTimestamp)
def update(sequenceNr: Long, persistenceId: String, deleted: Boolean, sender: ActorRef, writerUuid: String) =
copy(
sequenceNr = sequenceNr,
@ -195,4 +211,25 @@ private[persistence] final case class PersistentImpl(
sender = sender,
writerUuid = writerUuid)
override def hashCode(): Int = {
var result = HashCode.SEED
result = HashCode.hash(result, payload)
result = HashCode.hash(result, sequenceNr)
result = HashCode.hash(result, persistenceId)
result = HashCode.hash(result, manifest)
result = HashCode.hash(result, deleted)
result = HashCode.hash(result, sender)
result = HashCode.hash(result, writerUuid)
// timestamp not included in equals for backwards compatibility
result
}
override def equals(obj: Any): Boolean = obj match {
case other: PersistentImpl =>
payload == other.payload && sequenceNr == other.sequenceNr && persistenceId == other.persistenceId &&
manifest == other.manifest && deleted == other.deleted &&
sender == other.sender && writerUuid == other.writerUuid // timestamp not included in equals for backwards compatibility
case _ => false
}
}

View file

@ -166,7 +166,7 @@ private[persistence] trait LeveldbStore
def persistentFromBytes(a: Array[Byte]): PersistentRepr = serialization.deserialize(a, classOf[PersistentRepr]).get
private def addToMessageBatch(persistent: PersistentRepr, tags: Set[String], batch: WriteBatch): Unit = {
val persistentBytes = persistentToBytes(persistent)
val persistentBytes = persistentToBytes(persistent.withTimestamp(System.currentTimeMillis()))
val nid = numericId(persistent.persistenceId)
batch.put(keyToBytes(counterKey(nid)), counterToBytes(persistent.sequenceNr))
batch.put(keyToBytes(Key(nid, persistent.sequenceNr, 0)), persistentBytes)

View file

@ -168,6 +168,7 @@ class MessageSerializer(val system: ExtendedActorSystem) extends BaseSerializer
builder.setSequenceNr(persistent.sequenceNr)
// deleted is not used in new records from 2.4
if (persistent.writerUuid != Undefined) builder.setWriterUuid(persistent.writerUuid)
if (persistent.timestamp > 0L) builder.setTimestamp(persistent.timestamp)
builder
}
@ -197,7 +198,7 @@ class MessageSerializer(val system: ExtendedActorSystem) extends BaseSerializer
//
private def persistent(persistentMessage: mf.PersistentMessage): PersistentRepr = {
PersistentRepr(
val repr = PersistentRepr(
payload(persistentMessage.getPayload),
persistentMessage.getSequenceNr,
if (persistentMessage.hasPersistenceId) persistentMessage.getPersistenceId else Undefined,
@ -206,6 +207,8 @@ class MessageSerializer(val system: ExtendedActorSystem) extends BaseSerializer
if (persistentMessage.hasSender) system.provider.resolveActorRef(persistentMessage.getSender)
else Actor.noSender,
if (persistentMessage.hasWriterUuid) persistentMessage.getWriterUuid else Undefined)
if (persistentMessage.hasTimestamp) repr.withTimestamp(persistentMessage.getTimestamp) else repr
}
private def atomicWrite(atomicWrite: mf.AtomicWrite): AtomicWrite = {