Practical .NET Remoting Caching Sink






4.36/5 (11 votes)
Jun 28, 2003
2 min read

60853

511
A cache mechanism for .NET Remoting
Introduction
.NET Remoting solutions can be slow because of unnecessary "chatty" calls.
For programmers, making a method call is easier than creating sockets, preparing
buffers, calling send()
and recv()
, and then parsing
the result. So much easier that he often forgets about performance. Another
reason is that when we want to use PropertyGrid
to present a
remoted (MBR) object: A lot of property get/set methods are called, some of them
are unnecessary, but we can't help because we can't modify code inside
PropertyGrid
, yet we want to use this powerful control.
By using this caching sink, the task of optimizing performance of Remoting projects can be greatly simplified to just placing some attributes. The caching sink works on the client side by returning previous result if the server indicated the client can do so. Methods can have "caching attributes", indicating how long clients can cache the result.
How to use
Configuration Files
Here is the remoting config file that shows where to insert these sinks.
<serverProviders>
<formatter ref="binary" />
<!-- Sets X-Cache from CacheTime attribute of the method -->
<provider type=
"System.Runtime.Remoting.Caching.CachingServerSinkProvider, RemotingCache" />
</serverProviders>
<clientProviders>
<formatter ref="binary" />
<!-- return cached results if applicable -->
<provider type=
"System.Runtime.Remoting.Caching.CachingClientSinkProvider, RemotingCache" />
</clientProviders>
Of course, you need to compile the source code and make RemotingCache.dll.
Caching Attributes
using System.Runtime.Remoting.Caching;
//CacheTime
[CacheTime("00:01:00")]
public override string ToString() {
return "some string that won't change for one minute";
}
public string Name {
[CacheTime("01:00:00")]
get{
return "some property that won't change for one hour";
}
}
//InfinitelyCached
public string Name {
[InfinitelyCached]
get{
return "some property that won't change forever";
}
}
How it works
The sink on the server side is easy: it simply reads the caching attributes
on the method being called and return the information in form of "X-Cache" entry
in the response header. the string in this entry will be passed to
TimeSpan.Parse()
on the client side (so the format("hh:mm:ss")
should be used in the CacheTime
attribute).
The most difficult part in implementing this caching mechanism is on the client side. It is the part of designing a hash-key scheme so that only the exact same call matches the entry, where the previous result is stored. I'll briefly explain the design taken in this work. The hash-key has two part:
- The object Uri, typename and the method name.
- The request stream.( Note because the sink is placed after formatter, all
information in
IMessage
is contained in Request stream, soIMessage
can totally be ignored )
When the two hash keys are compared, the first part is compared first. So when the call is on a different object, or it has a different method name, the comparison will end without comparing the whole data in request (e.g. parameters).
For hash-key generation, there are other approaches that use
BinaryFormatter
to serialize the request IMessage
, and
store the serialized "opaque" data as key. This proves not only to be time
consuming (thus defying the purpose of performance improvement by caching), but
also extremely unstable -- The binary formatter seems to be unable to serialize
IMessage
: it throws ExecutionEngineException
which is
un-catchable and brings the whole execution environment down.
By placing the sink AFTER formatter in the client sink provider chain, My solution does not need to do serialization to calculate the key.