Wcf Httptransport: Streamed VS Buffered Transfermode

WCF HttpTransport: streamed vs buffered TransferMode

As you use 'GZipMessageEncodingBindingElement', I assume you are using the MS GZIP sample.

Have a look at DecompressBuffer() in GZipMessageEncoderFactory.cs and you will understand what's going on in buffered mode.

For the sake of example, let's say you have a message of uncompressed size 50M, compressed size 25M.

DecompressBuffer will receive an 'ArraySegment buffer' param of (1) 25M size. The method will then create a MemoryStream, uncompress the buffer into it, using (2) 50M. Then it will do a MemoryStream.ToArray(), copying the memory stream buffer into a new (3) 50M big byte array. Then it takes another byte array from the BufferManager of AT LEAST (4) 50M+, in reality, it can be a lot more - in my case it was always 67M for a 50M array.

At the end of DecompressBuffer, (1) will be returned to the BufferManager (which seems to never get cleared by WCF), (2) and (3) are subject to GC (which is async, and if you are faster than the GC, you might get OOM exceptions even though there would be enough mem if cleaned up). (4) will presumably be given back to the BufferManager in your BinaryMessageEncodingBindingElement.ReadMessage().

To sum up, for your 50M message, your buffered scenario will temporarily take up 25 + 50 + 50 + e.g. 65 = 190M memory, some of it subject to asynchronous GC, some of it managed by the BufferManager, which - worst case - means it keeps lots of unused arrays in memory that are neither usable in a subsequent request (e.g. too small) nor eligible for GC. Now imagine you have multiple concurrent requests, in that case BufferManager will create separate buffers for all concurrent requests, which will never be cleaned up, unless you manually call BufferManager.Clear(), and I don't know of a way to do that with the buffer managers used by WCF, see also this question: How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory? ]

Update: After migrating to IIS7 Http Compression ( wcf conditional compression) memory consumption, cpu load and startup time dropped (don't have the numbers handy) and then migrating from buffered to streamed TransferMode ( How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory?) memory consumption of my WCF client app has dropped from 630M (peak) / 470M (continuous) to 270M (both peak and continuous)!

Is changing from wcf binding transferMode from Buffered to Streamed considered a breaking change for the client?

To the best of my knowledge, WCF streamed mode transfer is opt-in at the client, meaning that even if you change it at the server, unless the client changes their end as well they'll still receive the stream in its entirety before serving it as a buffered chunk of data. In other words, it should be transparent to your clients, but will enable them to opt-in to a streamed response.

WCF: Streamed TransferMode and Method Return Types other than Stream and Message

Finally found something relevant.

The DataSet is an inherited child of IXMLSerializable, hence its a candidate for streaming. Following is picked from MSDN (Streaming Message Transfer)

Operations that occur across a streamed transport can have a contract with at most one input or output parameter. That parameter corresponds to the entire body of the message and must be a Message, a derived type of Stream, or an IXmlSerializable implementation. Having a return value for an operation is equivalent to having an output parameter.

and DataSet is an implementation of IXmlSerializable . DataSet's definition

[SerializableAttribute]
public class DataSet : MarshalByValueComponent, IListSource,
IXmlSerializable, ISupportInitializeNotification, ISupportInitialize,ISerializable

Thanks,

A

TimeoutException when TransferMode=Streamed

I could not exactly figure out the cause of the issue, but I was able to fix the issue and getting rid of excessive memory consumption etc. by doing the following.

After getting rid of GzipMessageEncoder (see wcf conditional compression for how to replace it by IIS' builtin compression), which is a monstrosity (see my answer on WCF HttpTransport: streamed vs buffered TransferMode), it was easy to switch to streamed TransferMode: How can I prevent BufferManager / PooledBufferManager in my WCF client app from wasting memory?.

WCF REST client with TransferMode=Streamed - logging entire (raw) http requests/responses

I can't find any reference right now, but it's a known fact that you cannot capture the contents of a streamed message to WCF tracing. When streaming is enabled, only the headers of the message will be traced.

Here's the source: Configuring Message Logging on MSDN

See towards the end of the page:

Service Level

Messages logged at this layer are
about to enter (on receiving) or leave
(on sending) user code. If filters
have been defined, only messages that
match the filters are logged.
Otherwise, all messages at the service
level are logged. Infrastructure
messages (transactions, peer channel,
and security) are also logged at this
level, except for Reliable Messaging
messages. On streamed messages, only
the headers are logged.
In addition,
secure messages are logged decrypted
at this level.

Are WCF data streaming restrictions enforced?

What is going on currently is the following: you enabled streaming on the HTTP transport, so the transport isn't buffering anything - the transport receives a Message object, and writes it out to the transport stream (wrapped in an XML writer) directly. However, the parameters to the operation are buffered before they're serialized so you're "paying" the memory usage which you would were the transfer mode set to 'Buffered'. On the wire, the difference is that the response from this service, instead of having a Content-Length header, will be chunked (i.e., it'll have a Transfer-Encoded: chunked header, and the body will be formatted accordingly. But it will work just as well.

I'd say that the article in MSDN could have chosen better words. In order for an operation to take advantage of streaming, it needs to have one single parameter, often of type Stream, Message or some type which implements IXmlSerializable. But a simple contract (i.e., [OperationContract] int Add(int x, int y)) will work just as well. And I'd imagine that it'll continue working like that, since it's perfectly valid for one single contract to have "normal" operations, and operations which take advantage of streaming, and since the transfer mode needs to be set per endpoint (and not per operation), it has to work for "simple" operations as well.

How can I tell when a WCF operation is being called

I found the answer here,

http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/f6541134-2e08-4eb9-987b-2158fb99b403

So there are various WCF limits which I had already handled correctly like this,

<wsHttpBinding>
<!-- The upload limit should be 5 Mb, rounding up to 6 Mb just to be sure -->
<binding name="CarWebserviceBinding" maxReceivedMessageSize="6291456">
<security mode="None">
</security>
<!-- The upload limit should be 5 Mb, rounding up to 6 Mb just to be sure -->
<readerQuotas maxArrayLength="6291456" />
</binding>
</wsHttpBinding>

BUT what I HADN'T done was this,

<system.web>
<httpRuntime maxRequestLength="16384" /> <!-- 16MB -->
</system.web>

IIS has a default limit of around 4MB for any http requests that come in. The above settings overrides that limit.

Another thing to mention is that I added this to my web.config to turn on tracing,

<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
<listeners>
<add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\logs\Traces.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>

And in the error log turned up the error,

CommunicationException - Maximum request length exceeded

Client aborted when WCF return Stream

After a bunch of debugging, i've found the problem. In my WCF service code i've implemented the stream ressource release after the try - catch:

    finally
{
if (file != null)
{
file.Close();
file = null;
}
}

The Stream was closing on the WCF side while the client was receiving the Stream.

This is where i found my answer:
IOException on streamed file upload via WCF over HTTP



Related Topics



Leave a reply



Submit