@@ -1993,6 +1993,7 @@ public async ValueTask ReadAsync(IJournalStorageConsumer consumer, CancellationT
19931993
19941994 private sealed class CapturingStorage : IJournalStorage
19951995 {
1996+ private readonly object _lock = new ( ) ;
19961997 private readonly List < byte [ ] > _segments = [ ] ;
19971998 private int _activeAppends ;
19981999
@@ -2004,7 +2005,16 @@ private sealed class CapturingStorage : IJournalStorage
20042005
20052006 public List < string > OperationLog { get ; } = [ ] ;
20062007
2007- public byte [ ] RecoverableBytes => _segments . SelectMany ( static segment => segment ) . ToArray ( ) ;
2008+ public byte [ ] RecoverableBytes
2009+ {
2010+ get
2011+ {
2012+ lock ( _lock )
2013+ {
2014+ return _segments . SelectMany ( static segment => segment ) . ToArray ( ) ;
2015+ }
2016+ }
2017+ }
20082018
20092019 public bool BlockNextAppend { get ; set ; }
20102020
@@ -2042,7 +2052,13 @@ private sealed class CapturingStorage : IJournalStorage
20422052
20432053 public int ReadConsumeCount { get ; private set ; }
20442054
2045- public void ResetReadConsumeCount ( ) => ReadConsumeCount = 0 ;
2055+ public void ResetReadConsumeCount ( )
2056+ {
2057+ lock ( _lock )
2058+ {
2059+ ReadConsumeCount = 0 ;
2060+ }
2061+ }
20462062
20472063 public bool IsCompactionRequested { get ; set ; }
20482064
@@ -2068,10 +2084,16 @@ public async ValueTask ReadAsync(IJournalStorageConsumer consumer, CancellationT
20682084 await AllowBlockedRead . Task . WaitAsync ( cancellationToken ) ;
20692085 }
20702086
2087+ byte [ ] [ ] segments ;
2088+ lock ( _lock )
2089+ {
2090+ segments = _segments . ToArray ( ) ;
2091+ }
2092+
20712093 if ( ConcatenateReads )
20722094 {
20732095 var totalLength = 0 ;
2074- foreach ( var segment in _segments )
2096+ foreach ( var segment in segments )
20752097 {
20762098 cancellationToken . ThrowIfCancellationRequested ( ) ;
20772099 totalLength += segment . Length ;
@@ -2081,13 +2103,17 @@ public async ValueTask ReadAsync(IJournalStorageConsumer consumer, CancellationT
20812103 {
20822104 var concatenated = new byte [ totalLength ] ;
20832105 var offset = 0 ;
2084- foreach ( var segment in _segments )
2106+ foreach ( var segment in segments )
20852107 {
20862108 segment . CopyTo ( concatenated . AsSpan ( offset ) ) ;
20872109 offset += segment . Length ;
20882110 }
20892111
2090- ReadConsumeCount ++ ;
2112+ lock ( _lock )
2113+ {
2114+ ReadConsumeCount ++ ;
2115+ }
2116+
20912117 consumer . Read ( concatenated , metadata : null , complete : true ) ;
20922118 }
20932119 else
@@ -2102,10 +2128,14 @@ public async ValueTask ReadAsync(IJournalStorageConsumer consumer, CancellationT
21022128
21032129 IEnumerable < ReadOnlyMemory < byte > > GetSegments ( )
21042130 {
2105- foreach ( var segment in _segments )
2131+ foreach ( var segment in segments )
21062132 {
21072133 cancellationToken . ThrowIfCancellationRequested ( ) ;
2108- ReadConsumeCount ++ ;
2134+ lock ( _lock )
2135+ {
2136+ ReadConsumeCount ++ ;
2137+ }
2138+
21092139 yield return segment ;
21102140 }
21112141 }
@@ -2114,16 +2144,28 @@ IEnumerable<ReadOnlyMemory<byte>> GetSegments()
21142144 public async ValueTask ReplaceAsync ( ReadOnlySequence < byte > value , CancellationToken cancellationToken )
21152145 {
21162146 cancellationToken . ThrowIfCancellationRequested ( ) ;
2117- ReplaceAttemptCount ++ ;
2118- if ( NextReplaceException is { } exception )
2147+ Exception ? exceptionToThrow ;
2148+ lock ( _lock )
21192149 {
2120- NextReplaceException = null ;
2121- FailedReplaceAttempts . Add ( value . ToArray ( ) ) ;
2122- OperationLog . Add ( "replace-failed" ) ;
2123- ExceptionDispatchInfo . Throw ( exception ) ;
2150+ ReplaceAttemptCount ++ ;
2151+ exceptionToThrow = NextReplaceException ;
2152+ if ( exceptionToThrow is not null )
2153+ {
2154+ NextReplaceException = null ;
2155+ FailedReplaceAttempts . Add ( value . ToArray ( ) ) ;
2156+ OperationLog . Add ( "replace-failed" ) ;
2157+ }
2158+ else
2159+ {
2160+ OperationLog . Add ( "replace" ) ;
2161+ }
2162+ }
2163+
2164+ if ( exceptionToThrow is not null )
2165+ {
2166+ ExceptionDispatchInfo . Throw ( exceptionToThrow ) ;
21242167 }
21252168
2126- OperationLog . Add ( "replace" ) ;
21272169 ReplaceEntered . TrySetResult ( ) ;
21282170 if ( BlockNextReplace )
21292171 {
@@ -2138,9 +2180,12 @@ public async ValueTask ReplaceAsync(ReadOnlySequence<byte> value, CancellationTo
21382180 }
21392181
21402182 var bytes = value . ToArray ( ) ;
2141- Replaces . Add ( bytes ) ;
2142- _segments . Clear ( ) ;
2143- _segments . Add ( bytes ) ;
2183+ lock ( _lock )
2184+ {
2185+ Replaces . Add ( bytes ) ;
2186+ _segments . Clear ( ) ;
2187+ _segments . Add ( bytes ) ;
2188+ }
21442189 }
21452190
21462191 public async ValueTask AppendAsync ( ReadOnlySequence < byte > value , CancellationToken cancellationToken )
@@ -2149,14 +2194,26 @@ public async ValueTask AppendAsync(ReadOnlySequence<byte> value, CancellationTok
21492194 Interlocked . Increment ( ref _activeAppends ) ;
21502195 try
21512196 {
2152- if ( NextAppendException is { } exception )
2197+ Exception ? exceptionToThrow ;
2198+ lock ( _lock )
21532199 {
2154- NextAppendException = null ;
2155- OperationLog . Add ( "append-failed" ) ;
2156- ExceptionDispatchInfo . Throw ( exception ) ;
2200+ exceptionToThrow = NextAppendException ;
2201+ if ( exceptionToThrow is not null )
2202+ {
2203+ NextAppendException = null ;
2204+ OperationLog . Add ( "append-failed" ) ;
2205+ }
2206+ else
2207+ {
2208+ OperationLog . Add ( "append" ) ;
2209+ }
2210+ }
2211+
2212+ if ( exceptionToThrow is not null )
2213+ {
2214+ ExceptionDispatchInfo . Throw ( exceptionToThrow ) ;
21572215 }
21582216
2159- OperationLog . Add ( "append" ) ;
21602217 AppendEntered . TrySetResult ( ) ;
21612218 if ( BlockNextAppend )
21622219 {
@@ -2165,8 +2222,11 @@ public async ValueTask AppendAsync(ReadOnlySequence<byte> value, CancellationTok
21652222 }
21662223
21672224 var bytes = value . ToArray ( ) ;
2168- Appends . Add ( bytes ) ;
2169- _segments . Add ( bytes ) ;
2225+ lock ( _lock )
2226+ {
2227+ Appends . Add ( bytes ) ;
2228+ _segments . Add ( bytes ) ;
2229+ }
21702230 }
21712231 finally
21722232 {
@@ -2177,11 +2237,15 @@ public async ValueTask AppendAsync(ReadOnlySequence<byte> value, CancellationTok
21772237 public ValueTask DeleteAsync ( CancellationToken cancellationToken )
21782238 {
21792239 cancellationToken . ThrowIfCancellationRequested ( ) ;
2180- DeleteEnteredWhileAppendInProgress = Volatile . Read ( ref _activeAppends ) > 0 ;
2181- OperationLog . Add ( "delete" ) ;
2240+ lock ( _lock )
2241+ {
2242+ DeleteEnteredWhileAppendInProgress = Volatile . Read ( ref _activeAppends ) > 0 ;
2243+ OperationLog . Add ( "delete" ) ;
2244+ DeleteCount ++ ;
2245+ _segments . Clear ( ) ;
2246+ }
2247+
21822248 DeleteEntered . TrySetResult ( ) ;
2183- DeleteCount ++ ;
2184- _segments . Clear ( ) ;
21852249 return default ;
21862250 }
21872251 }
0 commit comments