Take the community feedback survey now.

Anders Hattestad
Oct 12, 2011
  4115
(4 votes)

A quick fix when saving to DSS takes a bit too much time

In a project I’m currently working on I need to update some DSS tables when a user is viewing a page. This is not a problem, but sometimes the save method takes a bit to much time.

Since the saving part of the data  not necessarily need to be done in that user thread, I have created a Lazy saver class that do the saving in a different thread.

All I needed to do is to implement my DDS classes with an interface ISaveMe and then save the item like this

Code Snippet
  1. public void SaveMe()
  2. {
  3.     Store.Save(this);
  4. }
  5. public void Save()
  6. {
  7.     LazyDSSSave.Current.AddToSave(this);
  8. }
  9. public static void Save(T item)
  10. {
  11.     LazyDSSSave.Current.AddToSave(item);
  12. }
  13. public static DynamicDataStore Store
  14. {
  15.     get
  16.     {
  17.         return DynamicDataStoreFactory.Instance.GetStore(typeof(T));
  18.     }
  19. }
Then the code bellow will start a new thread if needed an do the saving there.

It is important to remember that all exceptions in a thread that is not suppressed will make the app pool to restart. Therefore is very important to catch all errors.

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Collections;
  6. using System.Threading;
  7.  
  8. namespace Itera.Data
  9. {
  10.     public interface ISaveMe
  11.     {
  12.         void SaveMe();
  13.     }
  14.     public class LazyDSSSave
  15.     {
  16.         protected Queue ActiveQueue = new Queue();
  17.         ThreadStart backgroundJob;
  18.         Thread thread;
  19.         #region Current
  20.         private static LazyDSSSave _current = new LazyDSSSave();
  21.         public static LazyDSSSave Current
  22.         {
  23.             get
  24.             {
  25.                 if (_current == null)
  26.                     _current = new LazyDSSSave();
  27.                 return _current;
  28.             }
  29.         }
  30.         #endregion
  31.  
  32.         public void AddToSave(object action)
  33.         {
  34.  
  35.             lock (ActiveQueue.SyncRoot)
  36.             {
  37.                 if (!ActiveQueue.Contains(action))
  38.                 {
  39.                     ActiveQueue.Enqueue(action);
  40.                 }
  41.             }
  42.             EnsureBackgroundJob();
  43.         }
  44.  
  45.         object lockObject = new object();
  46.         #region EnsureBackgroundJob
  47.         public void EnsureBackgroundJob()
  48.         {
  49.             lock (lockObject)
  50.             {
  51.                 if (thread == null || backgroundJob == null || !thread.IsAlive)
  52.                 {
  53.                     if (this.backgroundJob == null)
  54.                     {
  55.                         this.backgroundJob = new ThreadStart(this.Worker);
  56.                         
  57.                     }
  58.                     if (this.thread == null || !thread.IsAlive)
  59.                     {
  60.                         this.thread = new Thread(this.backgroundJob);
  61.                         this.thread.IsBackground = true;
  62.                         this.thread.Start();
  63.                     }
  64.                 }
  65.             }
  66.         }
  67.         #endregion
  68.       
  69.         #region Worker
  70.  
  71.         private void Worker()
  72.         {
  73.             try
  74.             {
  75.                 while (true)
  76.                 {
  77.                     object item = null;
  78.                     lock (ActiveQueue.SyncRoot)
  79.                     {
  80.                         if (ActiveQueue.Count == 0)
  81.                             return;
  82.                         item = ActiveQueue.Dequeue();
  83.                         
  84.                     }
  85.                     try
  86.                     {
  87.                         if (item is ISaveMe)
  88.                             (item as ISaveMe).SaveMe();
  89.                     }
  90.                     catch (Exception exception)
  91.                     {
  92.                        
  93.                         //log.Error("", exception);
  94.                     }
  95.                 }
  96.             }
  97.             catch (System.Exception error)
  98.             {
  99.                 //log.Error("", exception);
  100.             }
  101.             finally
  102.             {
  103.                 lock (lockObject)
  104.                 {
  105.                     if (backgroundJob != null)
  106.                         backgroundJob = null;
  107.                 }
  108.             }
  109.         }
  110.         #endregion
  111.     }
  112. }

 

This saves me sometimes as much as  70-100ms on each page request.

Oct 12, 2011

Comments

Magnus Rahl
Magnus Rahl Oct 12, 2011 01:15 PM

Any special reason you decided to build your own queueing/thread solution instead of using the ThreadPool?

Anders Hattestad
Anders Hattestad Oct 12, 2011 01:21 PM

Its based on the EPiServer.LazyIndexer code (at least some time ago)
I have used that way of handling this type of problems before, but I see your point :)
Its imo easier to add some statistics and logging here that can be viewed in a report later since you have one place to store these kind of data.

Please login to comment.
Latest blogs
Content modeling for beginners

  Introduction Learning by Doing – Optimizely Build Series  is a YouTube series where I am building  a fictional  website called  TasteTrail , food...

Ratish | Sep 14, 2025 |

A day in the life of an Optimizely OMVP - Enhancing Search Relevance with Optimizely Graph: Synonyms and Pinned Results

When building search experiences for modern digital platforms, relevance is everything. Users expect search to understand their intent, even when...

Graham Carr | Sep 14, 2025

Optimizely CMS and HTML validation message: Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.

When using the W3C Markup Validation Service, some annoying information messages pop up because Optimizely CMS adds the trailing slash to...

Tomas Hensrud Gulla | Sep 14, 2025 |

Turbocharge your strings - a case of display channels

When doing a routine performance test, during a CMS 12 upgrade, I was able to achieve 95% performance improvement. Let's look at SearchValues with ...

Stefan Holm Olsen | Sep 14, 2025 |