Skip to content

Commit 835b432

Browse files
authored
test(journaling): cover legacy recovery contracts (#10959)
1 parent 3f0dad5 commit 835b432

2 files changed

Lines changed: 526 additions & 24 deletions

File tree

test/Orleans.Journaling.Tests/OrleansBinaryJournalBufferWriterTests.cs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,4 +609,196 @@ public TestPreservedJournalEntry(string formatKey, ReadOnlyMemory<byte> payload)
609609

610610
public string FormatKey { get; }
611611
}
612+
613+
[Fact]
614+
public void BinaryFormat_Replay_ParsesConcatenatedLegacyRecordsInCommandOrder()
615+
{
616+
var firstPayload = System.Text.Encoding.UTF8.GetBytes("first");
617+
var secondPayload = System.Text.Encoding.UTF8.GetBytes("second");
618+
using var firstRecord = CreateLegacyWriter(bodyLength: null, streamId: 1, commandVersion: 0, firstPayload);
619+
using var secondRecord = CreateLegacyWriter(bodyLength: null, streamId: 2, commandVersion: 0, secondPayload);
620+
using var firstRecordBytes = firstRecord.PeekSlice(firstRecord.Length);
621+
using var secondRecordBytes = secondRecord.PeekSlice(secondRecord.Length);
622+
using var data = CreateWriter([.. firstRecordBytes.ToArray(), .. secondRecordBytes.ToArray()]);
623+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
624+
var consumer = new CollectingConsumer();
625+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(1, 2));
626+
627+
((IJournalFormat)new OrleansBinaryJournalFormat(SessionPool)).Replay(reader, context);
628+
629+
Assert.Collection(
630+
consumer.Entries,
631+
entry =>
632+
{
633+
Assert.Equal(1U, entry.StreamId);
634+
Assert.Equal(firstPayload, entry.Payload);
635+
Assert.Equal("first", System.Text.Encoding.UTF8.GetString(entry.Payload));
636+
},
637+
entry =>
638+
{
639+
Assert.Equal(2U, entry.StreamId);
640+
Assert.Equal(secondPayload, entry.Payload);
641+
Assert.Equal("second", System.Text.Encoding.UTF8.GetString(entry.Payload));
642+
});
643+
Assert.Equal(0, reader.Length);
644+
}
645+
646+
[Fact]
647+
public void BinaryFormat_Replay_RejectsZeroLengthLegacyBodyWithoutConsumingInput()
648+
{
649+
using var data = CreateLegacyWriter(bodyLength: 0, streamId: null, commandVersion: null, []);
650+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
651+
var originalLength = reader.Length;
652+
var consumer = new CollectingConsumer();
653+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(1));
654+
655+
// A legacy varuint zero is encoded as 0x01, which the public format dispatches as V1 framing.
656+
var exception = Assert.Throws<InvalidOperationException>(
657+
() => ((IJournalFormat)new OrleansBinaryJournalFormat(SessionPool)).Replay(reader, context));
658+
659+
Assert.Equal(1, originalLength);
660+
Assert.Equal("Malformed binary journal entry stream at byte offset 0: truncated fixed-width entry header.", exception.Message);
661+
Assert.Empty(consumer.Entries);
662+
Assert.Equal(originalLength, reader.Length);
663+
}
664+
665+
[Fact]
666+
public void BinaryFormat_Replay_RejectsOversizedLegacyStreamIdWithoutConsumingInput()
667+
{
668+
const ulong oversizedStreamId = (ulong)uint.MaxValue + 1;
669+
using var data = CreateLegacyWriter(bodyLength: null, oversizedStreamId, commandVersion: 0, []);
670+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
671+
var originalLength = reader.Length;
672+
var consumer = new CollectingConsumer();
673+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(1));
674+
675+
var exception = Assert.Throws<NotSupportedException>(
676+
() => ((IJournalFormat)new OrleansBinaryJournalFormat(SessionPool)).Replay(reader, context));
677+
678+
Assert.Equal("Unsupported legacy binary journal stream id at byte offset 0: 4294967296.", exception.Message);
679+
Assert.Empty(consumer.Entries);
680+
Assert.Equal(originalLength, reader.Length);
681+
}
682+
683+
[Fact]
684+
public void BinaryFormat_Replay_RejectsLegacyRecordMissingCommandVersionWithoutConsumingInput()
685+
{
686+
using var data = CreateLegacyWriter(bodyLength: null, streamId: 7, commandVersion: null, []);
687+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
688+
var originalLength = reader.Length;
689+
var consumer = new CollectingConsumer();
690+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(7));
691+
692+
var exception = Assert.Throws<InvalidOperationException>(
693+
() => ((IJournalFormat)new OrleansBinaryJournalFormat(SessionPool)).Replay(reader, context));
694+
695+
Assert.Equal("Malformed binary journal entry stream at byte offset 0: missing legacy command format version.", exception.Message);
696+
Assert.Empty(consumer.Entries);
697+
Assert.Equal(originalLength, reader.Length);
698+
}
699+
700+
[Fact]
701+
public void BinaryFormat_Replay_RejectsUnsupportedLegacyCommandVersionWithoutConsumingInput()
702+
{
703+
using var data = CreateLegacyWriter(bodyLength: null, streamId: 7, commandVersion: 1, [0xAA]);
704+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
705+
var originalLength = reader.Length;
706+
var consumer = new CollectingConsumer();
707+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(7));
708+
709+
var exception = Assert.Throws<NotSupportedException>(
710+
() => ((IJournalFormat)new OrleansBinaryJournalFormat(SessionPool)).Replay(reader, context));
711+
712+
Assert.Equal("Unsupported legacy binary journal command format version at byte offset 0: 1.", exception.Message);
713+
Assert.Empty(consumer.Entries);
714+
Assert.Equal(originalLength, reader.Length);
715+
}
716+
717+
private static ArcBufferWriter CreateLegacyWriter(uint? bodyLength, ulong? streamId, byte? commandVersion, ReadOnlySpan<byte> payload)
718+
{
719+
var writer = new ArcBufferWriter();
720+
var serializerWriter = Writer.Create(writer, session: null!);
721+
serializerWriter.WriteVarUInt32(bodyLength ?? checked((uint)(
722+
(streamId.HasValue ? GetVarUInt64ByteCount(streamId.Value) : 0)
723+
+ (commandVersion.HasValue ? 1 : 0)
724+
+ payload.Length)));
725+
726+
if (streamId.HasValue)
727+
{
728+
serializerWriter.WriteVarUInt64(streamId.Value);
729+
}
730+
731+
if (commandVersion.HasValue)
732+
{
733+
serializerWriter.WriteByte(commandVersion.Value);
734+
}
735+
736+
serializerWriter.Commit();
737+
writer.Write(payload);
738+
return writer;
739+
}
740+
741+
private static int GetVarUInt64ByteCount(ulong value)
742+
{
743+
var result = 1;
744+
while (value >= 128)
745+
{
746+
value >>= 7;
747+
result++;
748+
}
749+
750+
return result;
751+
}
752+
753+
[Fact]
754+
public void BinaryFormat_Replay_DispatchesConcatenatedV0AndV1RecordsInPhysicalOrder()
755+
{
756+
var legacyPayload = System.Text.Encoding.UTF8.GetBytes("legacy");
757+
var currentPayload = System.Text.Encoding.UTF8.GetBytes("current");
758+
using var legacyRecord = CreateLegacyWriter(bodyLength: null, streamId: 1, commandVersion: 0, legacyPayload);
759+
using var currentRecord = new OrleansBinaryJournalBufferWriter();
760+
AppendEntry(currentRecord.CreateJournalStreamWriter(new JournalStreamId(1)), currentPayload);
761+
using var legacyRecordBytes = legacyRecord.PeekSlice(legacyRecord.Length);
762+
using var data = CreateWriter([.. legacyRecordBytes.ToArray(), .. ToArray(currentRecord)]);
763+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
764+
var consumer = new CollectingConsumer();
765+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(1));
766+
IJournalFormat format = new OrleansBinaryJournalFormat(SessionPool);
767+
768+
format.Replay(reader, context);
769+
770+
Assert.Collection(
771+
consumer.Entries,
772+
entry =>
773+
{
774+
Assert.Equal(1U, entry.StreamId);
775+
Assert.Equal(legacyPayload, entry.Payload);
776+
Assert.Equal("legacy", System.Text.Encoding.UTF8.GetString(entry.Payload));
777+
},
778+
entry =>
779+
{
780+
Assert.Equal(1U, entry.StreamId);
781+
Assert.Equal(currentPayload, entry.Payload);
782+
Assert.Equal("current", System.Text.Encoding.UTF8.GetString(entry.Payload));
783+
});
784+
Assert.Equal(0, reader.Length);
785+
}
786+
787+
[Fact]
788+
public void BinaryFormat_Replay_UnsupportedLegacyCommandVersionReportsContextWithoutPartialApplication()
789+
{
790+
using var data = CreateLegacyWriter(bodyLength: null, streamId: 1, commandVersion: 1, [0xAA]);
791+
var reader = new JournalBufferReader(data.Reader, isCompleted: true);
792+
var originalLength = reader.Length;
793+
var consumer = new CollectingConsumer();
794+
var context = JournalTestReplayContext.Create(OrleansBinaryJournalFormat.JournalFormatKey, consumer.Bind(1));
795+
IJournalFormat format = new OrleansBinaryJournalFormat(SessionPool);
796+
797+
var exception = Assert.Throws<NotSupportedException>(() => format.Replay(reader, context));
798+
799+
Assert.Equal("Unsupported legacy binary journal command format version at byte offset 0: 1.", exception.Message);
800+
Assert.Null(exception.InnerException);
801+
Assert.Empty(consumer.Entries);
802+
Assert.Equal(originalLength, reader.Length);
803+
}
612804
}

0 commit comments

Comments
 (0)