I've known the folks at ScaleOut Software since 2005. Our team used their StateServer product to manage in memory session
state across web farms. Just a simple web.config change and installing the product
on each server was all it took to get going. No training, special server configuration,
or hassle. It is one of those applications that just works.
What problems did ScaleOut StateServer (SOSS) solve 5 years ago?
We bought SOSS initially to resolve issues with losing session when the app pool
recycles as well as a major business to business traffic issue concerning network
proxy server farms. There was talk of a need to manage session in Java applications
too and SOSS's ability to be easily ported to Linux was a big plus.
More and more companies are having their outgoing network traffic routed through
network proxy server farms. Essentially, these proxy server farms are designed
to hide the real ip address of the visitor from the web server. Thus, the network
load balancer has the possibility of routing subsequent requests to other web
servers in the farm which don't have access to that session data. So, if you
were trying to figure out why quite a few of your users were intermittently losing
session for no apparent reason even though you have session affinity enabled,
this is likely the cause. You may even see this in the business to consumer market
since some ISPs route their traffic through these farms as well. The phone calls
from annoyed IT executives of some of our biggest clients stopped immediately
after deploying SOSS to production.
What's new in ScaleOut StateServer 5.0?
Flash forward to 2010 and ScaleOut Software is seemingly light years ahead of where
they were. Managing session state across web farms is kids stuff compared to
what they are doing with caching across distributed data grids.
To make a simplistic analogy, it is a little like having an ASP.NET Application Cache
that is replicated across any server, anywhere on your network, that is running
a SOSS instance on it. Just like in ASP.NET, you give the cache item a name and
shove objects in it.
However, unlike ASP.NET, the objects in cache can be seamlessly referenced across
application domains and across servers. In version 5.0 of StateServer, you can
even run LINQ queries against the cache across all of the servers in the data store. Can you imagine
writing LINQ queries to find the exact result sets you want across an in-memory data store that
spans across multiple servers? StateServer does this with lightning speed and
handles all of the complex merging operations for you. It is a bit like having
LINQ To SQL but instead you get LINQ to SOSS.
Connect SOSS up to a backing store like NHibernate or direct to SQL Server, Oracle,
DB2, or Informix and you've got a powerful and stable foundation for LINQ without
the worry of future changes to things like Microsoft's Entity Framework.
We don't run a web farm. So, why do we need SOSS?
ScaleOut StateServer isn't just for web farms. As an example, the code at the bottom
of this page demonstrates how SOSS can be used to share objects in its data store
between a windows service (shown here in the form of a console app for ease of
testing) and an ASP.NET application. The windows service acts as a listener waiting
for the ASP.NET app to simply add an object to the cache. When that occurs, the
windows service gets the object and processes it. In this case, the service changes
the object so that the next time the ASP.NET app retrieves it, it will see the
changes.
This application could easily be extended to multiple WCF services, windows forms
applications, or even Java applications. All connecting to the same in memory
data store and passing objects around. Of course, you could write all the plumbing
for this yourself via something like sockets or named pipes. However, I'd bet
you couldn't do it for less than the cost of licenses to ScaleOut and with as
little code, testing, or hassle.
What types of applications could benefit from SOSS?
- Reliable shopping cart applications. Whether it is used in a web farm or not, you
will not lose your shopping cart session if the app pool recycles or even if
IIS is completely restarted.
- ECommerce applications or any sort of real-time asset tracking. Any application that
has large volumes of real-time data that needs to be analyzed and rendered to
the UI with real-time updates is going to benefit. You could build incredibly
powerful real-time ecommerce visitor analysis engines and the means to make on
the fly changes to logic flows without bogging down the ecommerce website.
- High volume financial transaction web sites.
- High traffic social network oriented games.
- Any ASP.NET or WCF Service application that needs to offload an object for heavy
duty processing offline. This is immediately applicable to applications I'm working
on right now.
- NHibernate driven applications that could benefit from a middleware caching approach.
ScaleOut offers samples for using their NamedCache API directly with NHibernate.
It is incredibly easy to implement over existing NHibernate layers.
- Create your own grid computing cloud versus sending your data to other providers
such as Microsoft. You can also wire up SOSS to interact with Microsoft's cloud
via Windows Azure.
How do I know this will work and is reliable?
When comparing distributed data grid vendors like ScaleOut, there are a few questions
you need to ask each vendor and insist on proof "before" you spend
your money.
- Is there a single point of failure in the data replication and retrieval process? Some vendors use a form of load balancer to direct traffic to each server in the
data grid. ScaleOut uses a very different approach they refer to as a scalable
heart-beating algorithm. Think of it as peer to peer health monitor. If one or
more servers fail, ScaleOut just lets one of the other peers handle the request.
Each peer keeps a constant eye on its other peers and automatically makes adjustments
if one of them is unable to handle the request.
- What is the underlying technology used to manage the data in the store? Some vendors rely on 100% .NET managed code. ScaleOut originally chose not to in
order to avoid potential .NET garbage collection issues with extremely high volumes
of objects in the store. ScaleOut is aptly named in that scalability is going
to be far more about network cards, bandwidth, and server memory than it will
be about their software crashing under heavy volume. Having used their product
on numerous production web sites, I am keenly aware of how reliable it is. But,
don't take my word for it. Have ScaleOut help you set up a performance test on
your own equipment. This will immediately determine if your network, bandwidth,
and server hardware can handle the projected load.
- Does the vendor use any form of client-side caching to reduce unnecessary data transfers
over the network and repeated object deserialization? The client-side caching actually works as promised and accurately evaluates whether
objects need to be transferred or not.
- What kind of global redundancy is available? There is a Geo Server add-on that enables you to replicate StateServer farms all
over the world. So, if you think your volume will be comparable to big dogs like
Amazon.com, you'll want to check this out.
- Is a comprehensive set of management tools available? If your system admin people need a real-time view of the objects in the data store,
ScaleOut offers a powerful Management Console and an object browser.
- Can I use the store for more than just a simple in-memory cache? Aside from what has already been discussed, ScacleOut also offers a Grid Computing
Edition that is pretty impressive and offers map/reduce computation.
- Can we add new servers to the SOSS farm without changing our applications or involving
programmers? Yes, just install SOSS on the new servers. Existing SOSS servers will instantly
recognize the new servers and begin using them.
- Do I need to buy separate server hardware or can I run SOSS on my existing web servers? No, you do not need to run SOSS on separate servers. I've run SOSS on production
web servers without any issues at all.
- Is the product reliable enough to be considered the store of record? In my opinion, SOSS is reliable enough to use as the store of record. Of course,
you'd want to save the data permanently in a database but as long as the store
is running on one server, that record will still be in memory.
- Is customer retention a strong point? ScaleOut claims a maintenance agreement renewal rate of 87%. Based on my experience,
I can believe that.
Is ScaleOut's technical support good?
They don't have an online forum like Telerik, Syncfusion, or ComponentOne do. In
my experience with them, they prefer to establish a personal relationship with
you and attempt to understand what it is you want to accomplish. From there,
they'll give you detailed suggestions that often come with relevant code snippets
to get you going.
What didn't I like about SOSS?
Distributed data grid technologies like SOSS have their own vocabulary and methodologies
for doing things. What is commonplace to developers in this arena is a bit foreign
to those of us who are not. So, it isn't always "obvious" which code
samples are the right places for you to start or even which methods, events,
and properties will get you the result you were looking for. As you get more
accustomed to distributed data grid APIs, their terminology and techniques make
much more sense. So, take my advice above and email their support team (especially
if you are still in the trial phase) and ask for specific guidance of how best
to approach an application with x,y, and z functionality. It will save you a
ton of time and you'll be stunned at just how simple it was to wire up impressive
functionality.
Does ScaleOut offer Microsoft MVPs a free license?
Yes, they do. You'll want to contact ScaleOut for details at scaleoutsoftware.com.
Code Sample
Here's the code sample I referred to above how simple sharing objects across applications
can be. You can also download it here. The download includes 64 bit dlls as a reference. If you are running 32 bit, you'll
need to swap out references to soss_namedcache.dll and soss_svcdotnet.dll in
every project in the two solutions.
To run the sample, make sure the SOSS store is running (download trial). Then, start the console application first. It will show a command window with
the line "Listening" when it is ready. Then, launch the ASP.NET solution.
Notice what is written to the browser and to the console application's command
window. From there, you can refresh the ASP.NET page to watch it process objects.
I suggest putting breakpoints in each application to watch what happens under
the hood.
If you need to use the DataContract attribute for Silverlight data object classes
instead of Serializable, you can. You just have to use one of SOSS's custom serialization
provider settings instead. Contact support for a small code snippet.
ShareSample.dll
namespace ScaleOut
{
[Serializable]
public class Customer
{
public int CustomerID { get; set; }
public double CustomerTypeID { get; set; }
public string Description { get; set; }
public StateServerSetting StateServerSettings = new StateServerSetting();
public string CacheKeyPrefix
{
get { return "Customer-CustomerID="; }
}
public string CacheKey
{
get
{
return CacheKeyPrefix + CustomerID.ToString();
}
}
public Customer()
{
}
public Customer(int customerID)
{
this.CustomerID = customerID;
}
public Customer(int customerID, double customerTypeID, string description)
{
this.CustomerID = customerID;
this.CustomerTypeID = customerTypeID;
this.Description = description;
}
}
}
namespace ScaleOut
{
[Serializable]
public class StateServerSetting
{
public void SetServer(string server)
{
_server = server;
_lastUpdateTime = DateTime.Now;
}
private string _server = string.Empty;
public string Server
{
get
{
return _server;
}
}
private DateTime _lastUpdateTime = DateTime.MinValue;
public DateTime LastUpdateTime
{
get
{
return _lastUpdateTime;
}
}
}
}
namespace ScaleOut
{
public class CustomerBackingStoreAdapter : IBackingStore
{
public EventHandler Loaded;
public EventHandler Updated;
public object Load(CachedObjectId id)
{
return null;
}
public void Store(CachedObjectId id, object value)
{
if (Updated != null) Updated(value,EventArgs.Empty);
}
public void Erase(CachedObjectId id)
{
}
public CreatePolicy GetCreatePolicy(CachedObjectId id)
{
return null;
}
}
}
Console Application (Listener)
class NamedCacheSample
{
private static string _applicationName = "ConsoleListener";
static void Main(string[] args)
{
var cache = CacheFactory.GetCache("CustomerCache");
try
{
cache.DefaultCreatePolicy.BackingStoreMode
= BackingStoreAsyncPolicy.WriteBehind;
cache.DefaultCreatePolicy.BackingStoreInterval
= TimeSpan.FromSeconds(1);
var adapter = new CustomerBackingStoreAdapter();
adapter.Updated += new EventHandler(OnCustomerUpdated);
cache.SetBackingStoreAdapter(adapter,
new BackingStorePolicy(true,false, false));
}
catch (Exception ex)
{
Console.WriteLine("Error reading object: " + ex.Message);
}
Console.WriteLine("Listening...");
Console.ReadKey();
}
public static void OnCustomerUpdated(object sender, EventArgs e)
{
var customer = sender as Customer;
if (customer == null) return;
// Changes to the Customer object in the listener will fire
// the event again. So, we track that the change came from
this
// application to avoid processing our own logic against
again.
if (customer.StateServerSettings.Server == _applicationName)
{
Console.WriteLine("Customer last updated by this server. No need to process again.");
return;
}
Console.WriteLine("Updated: " + customer.CustomerID.ToString() + " " + customer.Description);
var cache = CacheFactory.GetCache("CustomerCache");
customer.Description = "Changed in console " + DateTime.Now.ToString();
customer.StateServerSettings.SetServer(_applicationName);
cache[customer.CacheKey] = customer;
}
}
ASP.NET Application (Client)
namespace ScaleOut
{
public partial class _default : System.Web.UI.Page
{
private string _applicationName = "ASP.NET Server";
protected void Page_Load(object sender, EventArgs e)
{
var cache = CacheFactory.GetCache("CustomerCache");
var customer = new Customer();
try
{
cache.DefaultCreatePolicy.BackingStoreMode
= BackingStoreAsyncPolicy.WriteBehind;
cache.DefaultCreatePolicy.BackingStoreInterval
= TimeSpan.FromSeconds(1);
customer = cache[new Customer(1).CacheKey] as Customer; // See if this customer exists
if (customer == null)
{
customer
= new Customer(1, 1, "ASP.NET Loaded " + DateTime.Now.ToString());
customer.StateServerSettings.SetServer(_applicationName);
cache[customer.CacheKey]
= customer;
Response.Write(customer.Description + "<br/>");
}
else
{
Response.Write(customer.Description + "<br/>");
customer.Description
= "ASP.NET Changed " + DateTime.Now.ToString();
customer.StateServerSettings.SetServer(_applicationName);
cache[customer.CacheKey]
= customer;
Response.Write(customer.Description + "<br/>");
}
}
catch (Exception ex)
{
Response.Write("<br/>Error reading object: " + ex.Message);
}
}
}
}