Description
Description
We have a customized implementation of HttpStream which under the covers reads data from unmanaged memory and writes it as JSON to the request body:
https://github.com/ravendb/ravendb/blob/v5.4/src/Raven.Client/Json/BlittableJsonContent.cs
More specifically we have SingleNodeBatchCommand
which uses BlittableJsonContent
:
This command is used in Raven ETL process to ELT data from one RavenDB database to another. It's used by Raven ETL process which a single, dedicated thread:
The problem is that we're getting randomly crashes, AVE or exceptions when ETL process is stopping while the HTTP request is being processed.
When reproducing it locally we're mostly getting NRE exception due to some internals being disposed (which deals with unmanaged memory hence often resulting in AVE). The stacktrace is always the same:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Sparrow.dll!Sparrow.Compression.VariableSizeEncoding.Read<int>(byte* input, out int offset, out bool success) Line 181 C#
Sparrow.dll!Sparrow.Compression.VariableSizeEncoding.Read<int>(byte* input, out int offset) Line 256 C#
Sparrow.dll!Sparrow.Json.BlittableJsonReaderBase.ReadVariableSizeInt(int pos, out int offset) Line 179 C#
Sparrow.dll!Sparrow.Json.BlittableJsonReaderObject.BlittableJsonReaderObject(int pos, Sparrow.Json.BlittableJsonReaderObject parent, Sparrow.Json.BlittableJsonToken type) Line 163 C#
Sparrow.dll!Sparrow.Json.BlittableJsonReaderObject.GetObject(Sparrow.Json.BlittableJsonToken type, int position, out bool isBlittableJsonReader) Line 967 C#
Sparrow.dll!Sparrow.Json.BlittableJsonReaderObject.GetPropertyByIndex(int index, ref Sparrow.Json.BlittableJsonReaderObject.PropertyDetails prop, bool addObjectToCache) Line 775 C#
Sparrow.dll!Sparrow.Json.AbstractBlittableJsonTextWriter.WriteObject(Sparrow.Json.BlittableJsonReaderObject obj) Line 116 C#
Sparrow.dll!Sparrow.Json.AbstractBlittableJsonTextWriter.WriteValue(Sparrow.Json.BlittableJsonToken token, object val) Line 166 C#
Sparrow.dll!Sparrow.Json.AbstractBlittableJsonTextWriter.WriteObject(Sparrow.Json.BlittableJsonReaderObject obj) Line 119 C#
Sparrow.dll!Sparrow.Json.BlittableJsonTextWriterExtensions.WriteArray(Sparrow.Json.AbstractBlittableJsonTextWriter writer, string name, System.Collections.Generic.IEnumerable<Sparrow.Json.BlittableJsonReaderObject> items) Line 119 C#
Raven.Client.dll!Raven.Client.Documents.Commands.Batches.SingleNodeBatchCommand.CreateRequest.AnonymousMethod__0(System.IO.Stream stream) Line 104 C#
Raven.Client.dll!Raven.Client.Json.BlittableJsonContent.SerializeToStreamAsync(System.IO.Stream stream, System.Net.TransportContext context) Line 33 C#
[Resuming Async Method]
System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Threading.Tasks.VoidTaskResult>.AsyncStateMachineBox<Raven.Client.Json.BlittableJsonContent.<SerializeToStreamAsync>d__2>.MoveNext(System.Threading.Thread threadPoolThread) Unknown
System.Private.CoreLib.dll!System.Runtime.CompilerServices.TaskAwaiter.OutputWaitEtwEvents.AnonymousMethod__12_0(System.Action innerContinuation, System.Threading.Tasks.Task innerTask) Unknown
System.Private.CoreLib.dll!System.Threading.Tasks.AwaitTaskContinuation.System.Threading.IThreadPoolWorkItem.Execute() Unknown
System.Private.CoreLib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
System.Private.CoreLib.dll!System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart() Unknown
[Async Call Stack]
[Async] System.Net.Http.dll!System.Net.Http.Http2Connection.Http2Stream.SendRequestBodyAsync(System.Threading.CancellationToken cancellationToken) Unknown
[Async] System.Net.Http.dll!System.Net.Http.HttpConnectionBase.LogExceptions.AnonymousMethod__23_0(System.Threading.Tasks.Task t, object state) Unknown
We have confirmed that while this is still being processed the ETL thread is already being stopped. So it didn't wait for the request to be completed / aborted.
Based on the above stacktrace it looks here is the relevant piece of code:
More specifically HttpConnectionBase.LogExceptions()
has comment:
but it doesn't look that the task is awaited:
Here it what we saw when we got the exception with debugger attached:
Is it intentional that requestBodyTask
task isn't awaited in that scenario?
Reproduction Steps
We managed to reproduce it locally with the usage of two RavenDB servers and ETL between them. It was reproduced only on HTTP2.
Expected behavior
The requestBodyTask
should be awaited.
Actual behavior
Not awaited task causes the continuation of the main thread resulting in crashes.
Regression?
No response
Known Workarounds
No response
Configuration
No response
Other information
No response