Skip to content

Commit eed492f

Browse files
committed
LB is now working quite well, with the test app all pages are published properly and all indexes are populated. Now need to run some more tests like bringing a new server online.
1 parent 1f56607 commit eed492f

File tree

6 files changed

+101
-72
lines changed

6 files changed

+101
-72
lines changed

src/Umbraco.Core/Sync/DatabaseServerMessenger.cs

Lines changed: 68 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,15 @@ internal DatabaseServerMessenger(bool enableDistCalls)
3131

3232
//TODO: we need to make sure we can read from the db here!
3333

34-
//if there's been nothing sync, perform a sync, this will store the latest id
34+
//if there's been nothing sync, perform a first sync, this will store the latest id
3535
if (_lastId == -1)
3636
{
37-
Sync();
37+
FirstSync();
3838
}
3939
}
4040

41-
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
42-
private int _lastId = -1;
41+
private readonly object _lock = new object();
42+
private volatile int _lastId = -1;
4343
private volatile bool _syncing = false;
4444
//this ensures that only one thread can possibly check for sync operations every 5 seconds
4545
private const int SyncTimeFrameSeconds = 5;
@@ -86,70 +86,84 @@ protected override void PerformDistributedCall(
8686
ApplicationContext.Current.DatabaseContext.Database.Insert(dto);
8787
}
8888

89-
internal void Sync()
89+
/// <summary>
90+
/// Save the latest id in the db as the last synced
91+
/// </summary>
92+
/// <remarks>
93+
/// THIS IS NOT THREAD SAFE
94+
/// </remarks>
95+
internal void FirstSync()
9096
{
91-
//already syncing, don't process
92-
if (_syncing) return;
93-
94-
if (_lastId == -1)
97+
//we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
98+
// server and it will need to rebuild it's own persisted cache. Currently in that case it is Lucene and the xml
99+
// cache file.
100+
LogHelper.Warn<DatabaseServerMessenger>("No last synced Id found, this generally means this is a new server/install. The server will adjust it's last synced id to the latest found in the database and will start maintaining cache updates based on that id");
101+
//go get the last id in the db and store it
102+
var lastId = ApplicationContext.Current.DatabaseContext.Database.ExecuteScalar<int>(
103+
"SELECT MAX(id) FROM umbracoCacheInstruction");
104+
if (lastId > 0)
95105
{
96-
using (new WriteLock(_lock))
97-
{
98-
//we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
99-
// server and it will need to rebuild it's own persisted cache. Currently in that case it is Lucene and the xml
100-
// cache file.
101-
LogHelper.Warn<DatabaseServerMessenger>("No last synced Id found, this generally means this is a new server/install. The server will adjust it's last synced id to the latest found in the database and will start maintaining cache updates based on that id");
102-
//go get the last id in the db and store it
103-
var lastId = ApplicationContext.Current.DatabaseContext.Database.ExecuteScalar<int>(
104-
"SELECT MAX(id) FROM umbracoCacheInstruction");
105-
if (lastId > 0)
106-
{
107-
SaveLastSynced(lastId);
108-
}
109-
return;
110-
}
106+
SaveLastSynced(lastId);
111107
}
108+
}
112109

113-
//don't process, this is not in the timeframe
114-
if (TimeSpan.FromTicks(DateTime.UtcNow.Ticks).TotalSeconds - TimeSpan.FromTicks(_lastUtcTicks).TotalSeconds <= SyncTimeFrameSeconds)
115-
return;
110+
internal void Sync()
111+
{
116112

117-
using (new WriteLock(_lock))
113+
//don't process, this is not in the timeframe - we don't want to check the db every request, only once if it's been at least 5 seconds.
114+
if (TimeSpan.FromTicks(DateTime.UtcNow.Ticks).TotalSeconds - TimeSpan.FromTicks(_lastUtcTicks).TotalSeconds <= SyncTimeFrameSeconds)
118115
{
119-
//set the flag so other threads don't attempt
120-
_syncing = true;
121-
_lastUtcTicks = DateTime.UtcNow.Ticks;
122-
123-
//get the outstanding items
124-
125-
var sql = new Sql().Select("*")
126-
.From<CacheInstructionDto>()
127-
.Where<CacheInstructionDto>(dto => dto.Id > _lastId)
128-
.OrderBy<CacheInstructionDto>(dto => dto.Id);
129-
130-
var list = ApplicationContext.Current.DatabaseContext.Database.Fetch<CacheInstructionDto>(sql);
116+
//NOTE: Removed logging as it will just keep showing this and people will wonder why.
117+
//LogHelper.Debug<DatabaseServerMessenger>("Skipping distributed sync, not in timeframe");
118+
return;
119+
}
131120

132-
if (list.Count > 0)
121+
if (_syncing == false)
122+
{
123+
lock (_lock)
133124
{
134-
foreach (var item in list)
125+
if (_syncing == false)
135126
{
136-
try
137-
{
138-
var jsonArray = JsonConvert.DeserializeObject<JArray>(item.JsonInstruction);
139-
UpdateRefreshers(jsonArray);
140-
}
141-
catch (JsonException ex)
127+
//set the flag so other threads don't attempt
128+
_syncing = true;
129+
_lastUtcTicks = DateTime.UtcNow.Ticks;
130+
131+
using (DisposableTimer.DebugDuration<DatabaseServerMessenger>("Syncing from database..."))
142132
{
143-
LogHelper.Error<DatabaseServerMessenger>("Could not deserialize a distributed cache instruction! Value: " + item.JsonInstruction, ex);
133+
//get the outstanding items
134+
135+
var sql = new Sql().Select("*")
136+
.From<CacheInstructionDto>()
137+
.Where<CacheInstructionDto>(dto => dto.Id > _lastId)
138+
.OrderBy<CacheInstructionDto>(dto => dto.Id);
139+
140+
var list = ApplicationContext.Current.DatabaseContext.Database.Fetch<CacheInstructionDto>(sql);
141+
142+
if (list.Count > 0)
143+
{
144+
foreach (var item in list)
145+
{
146+
try
147+
{
148+
var jsonArray = JsonConvert.DeserializeObject<JArray>(item.JsonInstruction);
149+
UpdateRefreshers(jsonArray);
150+
}
151+
catch (JsonException ex)
152+
{
153+
LogHelper.Error<DatabaseServerMessenger>("Could not deserialize a distributed cache instruction! Value: " + item.JsonInstruction, ex);
154+
}
155+
}
156+
157+
SaveLastSynced(list.Max(x => x.Id));
158+
}
159+
160+
//reset
161+
_syncing = false;
144162
}
163+
145164
}
146-
147-
SaveLastSynced(list.Max(x => x.Id));
148165
}
149166
}
150-
151-
//reset
152-
_syncing = false;
153167
}
154168

155169
internal void UpdateRefreshers(JArray jsonArray)

src/Umbraco.Web.UI/config/ExamineSettings.config

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ More information and documentation can be found on CodePlex: http://umbracoexami
1212
<add name="InternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
1313
supportUnpublished="true"
1414
supportProtected="true"
15-
useTempStorage="Sync"
15+
1616
analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"/>
1717

1818
<add name="InternalMemberIndexer" type="UmbracoExamine.UmbracoMemberIndexer, UmbracoExamine"
1919
supportUnpublished="true"
2020
supportProtected="true"
21-
useTempStorage="Sync"
21+
2222
analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net"/>
2323

2424
<!-- default external indexer, which excludes protected and unpublished pages-->
2525
<add name="ExternalIndexer" type="UmbracoExamine.UmbracoContentIndexer, UmbracoExamine"
26-
useTempStorage="Sync" />
26+
/>
2727

2828
</providers>
2929
</ExamineIndexProviders>
@@ -32,14 +32,14 @@ More information and documentation can be found on CodePlex: http://umbracoexami
3232
<providers>
3333
<add name="InternalSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
3434
analyzer="Lucene.Net.Analysis.WhitespaceAnalyzer, Lucene.Net"
35-
useTempStorage="Sync"/>
35+
/>
3636

3737
<add name="ExternalSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
38-
useTempStorage="Sync"/>
38+
/>
3939

4040
<add name="InternalMemberSearcher" type="UmbracoExamine.UmbracoExamineSearcher, UmbracoExamine"
4141
analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net" enableLeadingWildcard="true"
42-
useTempStorage="Sync"/>
42+
/>
4343

4444
</providers>
4545
</ExamineSearchProviders>

src/Umbraco.Web.UI/config/umbracoSettings.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@
213213
</scheduledTasks>
214214

215215
<!-- distributed calls must be enabled when using Umbraco in a load balanced environment -->
216-
<distributedCall enable="false">
216+
<distributedCall enable="true">
217217
<!-- the id of the user who's making the calls -->
218218
<!-- needed for security, umbraco will automatically look up correct login and passwords -->
219219
<user>0</user>

src/Umbraco.Web/BatchedDatabaseServerMessenger.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,26 @@ internal BatchedDatabaseServerMessenger(bool enableDistCalls) : base(enableDistC
2121

2222
void UmbracoModule_RouteAttempt(object sender, Routing.RoutableAttemptEventArgs e)
2323
{
24-
if (e.Outcome == EnsureRoutableOutcome.NotDocumentRequest)
24+
switch (e.Outcome)
2525
{
26-
//so it's not a document request, we'll check if it's a back office request
27-
if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
28-
{
29-
30-
//it's a back office request, we should sync!
26+
case EnsureRoutableOutcome.IsRoutable:
3127
Sync();
32-
33-
}
28+
break;
29+
case EnsureRoutableOutcome.NotDocumentRequest:
30+
//so it's not a document request, we'll check if it's a back office request
31+
if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
32+
{
33+
//it's a back office request, we should sync!
34+
Sync();
35+
}
36+
break;
37+
case EnsureRoutableOutcome.NotReady:
38+
case EnsureRoutableOutcome.NotConfigured:
39+
case EnsureRoutableOutcome.NoContent:
40+
default:
41+
break;
3442
}
43+
3544
}
3645

3746
void UmbracoModule_EndRequest(object sender, EventArgs e)

src/Umbraco.Web/WebBootManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ protected override void InitializeResolvers()
322322

323323
//Override the ServerMessengerResolver to set a username/password for the distributed calls
324324
ServerMessengerResolver.Current.SetServerMessenger(new BatchedDatabaseServerMessenger(UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled));
325-
325+
326326
//ServerMessengerResolver.Current.SetServerMessenger(new DatabaseServerMessenger(UmbracoConfig.For.UmbracoSettings().DistributedCall.Enabled));
327327
//ServerMessengerResolver.Current.SetServerMessenger(new BatchedServerMessenger(() =>
328328
//{

src/Umbraco.Web/umbraco.presentation/content.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,17 @@ internal static XmlDocument AppendDocumentXml(int id, int level, int parentId, X
421421
// find the parent
422422
XmlNode parentNode = level == 1
423423
? xmlContentCopy.DocumentElement
424-
: xmlContentCopy.GetElementById(parentId.ToString());
424+
: xmlContentCopy.GetElementById(parentId.ToString(CultureInfo.InvariantCulture));
425425

426426
// no parent = cannot do anything
427427
if (parentNode == null)
428-
return xmlContentCopy;
428+
{
429+
//TODO: At least we are logging this now, before there was no indication of anything going wrong, but surely there's a better way to deal with this?
430+
// Since the document is out of date, we should probably rebuild it right?
431+
LogHelper.Warn<content>("No parent node could be found with parentID: " + parentId + " therefore the xml cache document could not be updated. The XML cache file is out of sync, you may need to delete the umbraco.config file and restart your web application");
432+
return xmlContentCopy;
433+
}
434+
429435

430436
// define xpath for getting the children nodes (not properties) of a node
431437
var childNodesXPath = UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema

0 commit comments

Comments
 (0)