Lock ShellDescriptor cache

The cache.dat files contains the enabled features for every tenants.
This file is accessed in read/write by all tenants when they are loaded
or changed. This can happen concurrently if tenants are activated
in parallel, if multiple servers are using the same file system, or if
multiple tenants get their features changed at the same time. This
commit doesn't fix the case of multiple web roles accessing the same
file system (WAWS).
This commit is contained in:
Sebastien Ros
2013-11-26 10:36:06 -08:00
parent 082b28204b
commit 0cce4b4357

View File

@@ -32,7 +32,7 @@ namespace Orchard.Environment.Descriptor {
public class ShellDescriptorCache : IShellDescriptorCache {
private readonly IAppDataFolder _appDataFolder;
private const string DescriptorCacheFileName = "cache.dat";
private static readonly object _synLock = new object();
public ShellDescriptorCache(IAppDataFolder appDataFolder) {
_appDataFolder = appDataFolder;
T = NullLocalizer.Instance;
@@ -47,48 +47,54 @@ namespace Orchard.Environment.Descriptor {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "StringReader closed by XmlReader.")]
public ShellDescriptor Fetch(string name) {
VerifyCacheFile();
var text = _appDataFolder.ReadFile(DescriptorCacheFileName);
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(text);
XmlNode rootNode = xmlDocument.DocumentElement;
if (rootNode != null) {
foreach (XmlNode tenantNode in rootNode.ChildNodes) {
if (String.Equals(tenantNode.Name, name, StringComparison.OrdinalIgnoreCase)) {
return GetShellDecriptorForCacheText(tenantNode.InnerText);
lock (_synLock) {
VerifyCacheFile();
var text = _appDataFolder.ReadFile(DescriptorCacheFileName);
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(text);
XmlNode rootNode = xmlDocument.DocumentElement;
if (rootNode != null) {
foreach (XmlNode tenantNode in rootNode.ChildNodes) {
if (String.Equals(tenantNode.Name, name, StringComparison.OrdinalIgnoreCase)) {
return GetShellDecriptorForCacheText(tenantNode.InnerText);
}
}
}
return null;
}
return null;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "writer closed by xmlWriter.")]
public void Store(string name, ShellDescriptor descriptor) {
VerifyCacheFile();
var text = _appDataFolder.ReadFile(DescriptorCacheFileName);
bool tenantCacheUpdated = false;
var saveWriter = new StringWriter();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(text);
XmlNode rootNode = xmlDocument.DocumentElement;
if (rootNode != null) {
foreach (XmlNode tenantNode in rootNode.ChildNodes) {
if (String.Equals(tenantNode.Name, name, StringComparison.OrdinalIgnoreCase)) {
tenantNode.InnerText = GetCacheTextForShellDescriptor(descriptor);
tenantCacheUpdated = true;
break;
lock (_synLock) {
VerifyCacheFile();
var text = _appDataFolder.ReadFile(DescriptorCacheFileName);
bool tenantCacheUpdated = false;
var saveWriter = new StringWriter();
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(text);
XmlNode rootNode = xmlDocument.DocumentElement;
if (rootNode != null) {
foreach (XmlNode tenantNode in rootNode.ChildNodes) {
if (String.Equals(tenantNode.Name, name, StringComparison.OrdinalIgnoreCase)) {
tenantNode.InnerText = GetCacheTextForShellDescriptor(descriptor);
tenantCacheUpdated = true;
break;
}
}
if (!tenantCacheUpdated) {
XmlElement newTenant = xmlDocument.CreateElement(name);
newTenant.InnerText = GetCacheTextForShellDescriptor(descriptor);
rootNode.AppendChild(newTenant);
}
}
if (!tenantCacheUpdated) {
XmlElement newTenant = xmlDocument.CreateElement(name);
newTenant.InnerText = GetCacheTextForShellDescriptor(descriptor);
rootNode.AppendChild(newTenant);
}
}
xmlDocument.Save(saveWriter);
_appDataFolder.CreateFile(DescriptorCacheFileName, saveWriter.ToString());
xmlDocument.Save(saveWriter);
_appDataFolder.CreateFile(DescriptorCacheFileName, saveWriter.ToString());
}
}
#endregion