Home » Blog

rounded header

Archive for November, 2009

Using a LightSpeed model against multiple databases

tag icon Tagged as LightSpeed

LightSpeed has supported multiple databases since those early days when it was carved on clay tablets and shipped by cart to eager customers across Sumeria. And it’s always been easy to use the same model in a different database: you just change this:

context.ConnectionString = "Data Source=store.db3";
context.DataProvider = DataProvider.Sqlite3;

to this:

context.ConnectionString = "Data Source=localhost;Initial Catalog=store;Integrated Security=True";
context.DataProvider = DataProvider.SqlServer2005;

In most cases — basically unless you have data type portability issues or provider-specific quirks such as different transaction support — you’ll be able to keep using your existing code without change from one database engine to another.

The LightSpeed designer extends that portability to design time. By changing the connection string and data provider on a model, and choosing Update Database, you can rapidly create much of your schema on the new database, rather than having to manually translate the SQL CREATE scripts from one dialect to another. In fact, this helps with portability of runtime code as well: the designer knows how to map the same CLR type (say Guid) into different SQL data types for different databases, so you’ll rarely run into problems with uniqueidentifier vs. char(36) or bit vs. tinyint. The schemas generated by the designer aren’t always perfect, but they’re a huge leg up. (The forthcoming migrations feature in LightSpeed 3 will allow you to take greater control of script generation, without sacrificing portability.)

The runtime support for multiple databases shouldn’t be too surprising — after all, one of the benefits of using an object-relational mapper is that it abstracts away the differences between databases. In fact, even Microsoft’s much-maligned Entity Framework allows you to move a model from one database to another, and you hardly need to copy and paste large chunks of XML or craft connection strings that look like Perl code at all. Design-time support however is less common, so if you need to develop schemas for multiple back ends, or if you’re prototyping against one engine but aren’t committed to it for your final product, LightSpeed would well be the product for you!

What’s the deal with LightSpeed caching?

tag icon Tagged as LightSpeed, Products

Recently a LightSpeed user asked for more information about second level caching – in particular there did not seem to be an abundance of information about how it worked in the documentation. We’re set to beef up the documentation to provide exciting bed time reading however Ivan suggested it would be interesting enough to blog about. So, without further ado, here’s a write up covering many of the questions that developers may have regarding LightSpeed and caching.

What’s the difference between first and second level caches?

A first level cache is a cache that is scoped only to the current unit of work. A second level cache is scoped across the entire process. A better way of showing what this means is to give a code example.

using(var uow = LightSpeedContext.Default.CreateUnitOfWork())
{
  var contrib1 = uow.FindById<Contribution>(52);
 
  // This request will be returned from the level one cache in memory
  var contrib2 = uow.FindById<Contribution>(52);
}
 
using (var uow = LightSpeedContext.Default.CreateUnitOfWork())
{
  // If level two cache is not enabled, this would generate
  // a database call. With the level two cache enabled it would
  // be fetched from the cache as we fetched it in the previous
  // unit of work
  var contrib1 = uow.FindById<Contribution>(52);
}

Can I turn off the first level cache?

No, this is a simple lookup mechanism baked into the unit of work. The second level cache however is disabled by default and it is your choice if you wish to use it.

Do I need a second level cache? When should I use one?

You do not need to use second level caching at all however you may wish to enable it to improve performance of your application. If you’re noticing that your application is querying the database frequently for the same information (for example, reference data) then adding caching will provide an improvement in performance immediately.

Web applications also benefit well from the second level cache as generally pages are constructed relatively quickly within a tightly defined unit of work and therefore the first level cache is not going to be of use across multiple requests. On the topic of caching with web applications, it’s important to note that you will likely be able to deliver much larger performance improvements by simply enabling output caching on your web pages so that no code execution (and therefore queries) are required on the server side.

Why wouldn’t I use a second level cache?

Great question – I’m glad you asked. Given the benefits of not having to query your database as often and simply fetch things from memory you would wonder why it is not enabled by default. The reason is that you want to avoid stale objects being returned. Cache expiry is a very complex topic in its own right and most caching products include many options for tuning cache item expiry (time based expiry, memory pressure expiry, etc).

Can I configure the second level cache?

Absolutely – caching can be configured on a per-entity basis. This is super handy for situations where data is largely static (e.g. reference data in look up tables) but not impacting highly volatile data such as statistics tracking entities. Further to this it is possible to configure a timespan for data to be stored in the cache before it expires and would be re-fetched from the database.

Like almost everything in LightSpeed, you can configure this both in the designer or in code.

Configure LightSpeed ORM cache

Or

[Cached]
public class Instance : Entity<long>
{
  // entity properties and other guff
}

When will I get a cache hit?

For the first level cache, LightSpeed will check if it already has an entity loaded for the given Id when you perform any type of fetch where you specify an ID. For example, if you use the FindById() method, LightSpeed will check the internal first level cache first and, if found, return that and save a database call. If an explicit ID is not provided (for example, a query that would return all entities from a table) then as LightSpeed receives the result and looks to hydrate an entity it will first check if it already has an entity in the first level cache and, if found, return that entity. This type of logic applies when fetching explicitly as well as when lazy loading occurs.

The second level cache, if enabled, will also be checked in all of the same situations however it is more likely to contain objects as they will live across the unit of work boundaries.

To summarise when a cache hit would occur:

  • You fetch an entity by Id
  • You fetch a collection of entities
  • You lazy load a collection or entity

When is an entity loaded into the cache?

An entity is loaded into the cache as soon as it is fetched from the database by LightSpeed.

What exactly is the second level cache? How does it manage the cache objects?

LightSpeed has always provided the cache as an extensibility point meaning that you have a choice in cache providers. Any class that implements the Mindscape.LightSpeed.Caching.ICache interface can be plugged in to provide your caching needs.

LightSpeed does ship with two cache providers out of the box however:

DefaultCache: This cache uses the System.Web.Caching.Cache class and, despite the namespace, is a perfectly good cache for all environments (web, desktop apps, etc). This is the most common and recommended second level caching choice for LightSpeed users. To leverage it all you need to do is set it on your LightSpeedContext.

MemcachedCache: The DefaultCache will handle most solutions however if you have really large caching needs then the MemcachedCache is the caching choice for you! Memcached is an open source caching system that uses large distributed hash tables that can span multiple servers. This is unlikely to be of use to most people but we have included it for larger customers and as an example of another cache provider.

How do I apply a caching provider?

The second level caching provider can be applied either in your .config file:

  <lightSpeedContexts>
    <add name="default" 
         connectionStringName="Dev" 
         cacheClass="Mindscape.LightSpeed.Caching.DefaultCache, Mindscape.LightSpeed" />
  </lightSpeedContexts>

or in code:

LightSpeedContext.Default.Cache = new CacheBroker(new DefaultCache());

Can I read items from the cache myself, without queries?

Absolutely, you can add, update and read items from the cache by accessing the UnitOfWork.Context.Cache object.

That’s it!

If you have any questions regarding LightSpeed caching please post a comment and I’ll be happy to answer. All feedback will directly influence the changes made to the documentation updates for LightSpeed 3.0.

Data Products Visual Controls Community Store
LightSpeed ORM
NHibernate Designer
SimpleDB Tools
SharePoint Tools
WPF Elements
WPF Diagrams
Silverlight Elements
Forums
Blog
Register
Login
Subscribe to newsletter
Buy Now
My Account
Volume Discounts
Purchase Orders
Contact Us