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
- public void SaveMe()
- {
- Store.Save(this);
- }
- public void Save()
- {
- LazyDSSSave.Current.AddToSave(this);
- }
- public static void Save(T item)
- {
- LazyDSSSave.Current.AddToSave(item);
- }
- public static DynamicDataStore Store
- {
- get
- {
- return DynamicDataStoreFactory.Instance.GetStore(typeof(T));
- }
- }
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.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Collections;
- using System.Threading;
- namespace Itera.Data
- {
- public interface ISaveMe
- {
- void SaveMe();
- }
- public class LazyDSSSave
- {
- protected Queue ActiveQueue = new Queue();
- ThreadStart backgroundJob;
- Thread thread;
- #region Current
- private static LazyDSSSave _current = new LazyDSSSave();
- public static LazyDSSSave Current
- {
- get
- {
- if (_current == null)
- _current = new LazyDSSSave();
- return _current;
- }
- }
- #endregion
- public void AddToSave(object action)
- {
- lock (ActiveQueue.SyncRoot)
- {
- if (!ActiveQueue.Contains(action))
- {
- ActiveQueue.Enqueue(action);
- }
- }
- EnsureBackgroundJob();
- }
- object lockObject = new object();
- #region EnsureBackgroundJob
- public void EnsureBackgroundJob()
- {
- lock (lockObject)
- {
- if (thread == null || backgroundJob == null || !thread.IsAlive)
- {
- if (this.backgroundJob == null)
- {
- this.backgroundJob = new ThreadStart(this.Worker);
- }
- if (this.thread == null || !thread.IsAlive)
- {
- this.thread = new Thread(this.backgroundJob);
- this.thread.IsBackground = true;
- this.thread.Start();
- }
- }
- }
- }
- #endregion
- #region Worker
- private void Worker()
- {
- try
- {
- while (true)
- {
- object item = null;
- lock (ActiveQueue.SyncRoot)
- {
- if (ActiveQueue.Count == 0)
- return;
- item = ActiveQueue.Dequeue();
- }
- try
- {
- if (item is ISaveMe)
- (item as ISaveMe).SaveMe();
- }
- catch (Exception exception)
- {
- //log.Error("", exception);
- }
- }
- }
- catch (System.Exception error)
- {
- //log.Error("", exception);
- }
- finally
- {
- lock (lockObject)
- {
- if (backgroundJob != null)
- backgroundJob = null;
- }
- }
- }
- #endregion
- }
- }
This saves me sometimes as much as 70-100ms on each page request.
Any special reason you decided to build your own queueing/thread solution instead of using the ThreadPool?
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.