Skip to content

Commit

Permalink
Added OpenQuestions.md file to log ideas and questions to address
Browse files Browse the repository at this point in the history
Fix 'PrevLogIndex' in AppendEntries so it properly covers empty log cornercase
Moved timer reinitialization at the end of event to prevent triggering too fast when timeout is set to 0
  • Loading branch information
dupdob committed Sep 22, 2013
1 parent 054b1a7 commit c55a3f1
Show file tree
Hide file tree
Showing 15 changed files with 164 additions and 64 deletions.
8 changes: 8 additions & 0 deletions OpenQuestions.md
@@ -0,0 +1,8 @@
** RAFTiNG Open questions **
=============================

This document list questions that should be answered in the course of the project.


** Stability **
- how can RAFTiNG handle configuration errors?
7 changes: 5 additions & 2 deletions RAFTiNG.Tests/Acceptance/BasicRaftSteps.cs
Expand Up @@ -35,11 +35,13 @@ public class BasicRaftSteps: IDisposable
{
private Middleware middleware;

private IDisposable logHandle;

private List<Node<string>> nodes;

static BasicRaftSteps()
public BasicRaftSteps()
{
Helpers.InitLog4Net();
logHandle = Helpers.InitLog4Net();
}

[Given(@"I have deployed (.*) instances")]
Expand Down Expand Up @@ -93,6 +95,7 @@ public void WhenIWaitSeconde(int p0)
/// </summary>
public void Dispose()
{
logHandle.Dispose();
foreach (var node in nodes)
{
node.Dispose();
Expand Down
26 changes: 21 additions & 5 deletions RAFTiNG.Tests/Helpers.cs
Expand Up @@ -14,6 +14,7 @@
// --------------------------------------------------------------------------------------------------------------------
namespace RAFTiNG.Tests
{
using System;
using System.Collections.Generic;

using log4net;
Expand Down Expand Up @@ -49,19 +50,34 @@ public static NodeSettings BuildNodeSettings(string nodeId, IEnumerable<string>
return settings;
}

internal static void InitLog4Net()
internal static IDisposable InitLog4Net()
{
var appender = new ConsoleAppender();
appender.Layout =
new PatternLayout("%date{HH:mm:ss,fff} %-5level - %message (%logger) [%thread]%newline");
appender.Threshold = Level.Trace;
var appender = new ConsoleAppender
{
Layout =
new PatternLayout(
"%date{HH:mm:ss,fff} %-5level - %message (%logger) [%thread]%newline"),
Threshold = Level.Trace
};
appender.ActivateOptions();

// Configure the root logger.
var h = (log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository();
var rootLogger = h.Root;
rootLogger.Level = h.LevelMap["TRACE"];
BasicConfigurator.Configure(appender);
return new LogWrapper();
}

private class LogWrapper: IDisposable
{
/// <summary>
/// Exécute les tâches définies par l'application associées à la libération ou à la redéfinition des ressources non managées.
/// </summary>
public void Dispose()
{
log4net.LogManager.Shutdown();
}
}
}
}
1 change: 1 addition & 0 deletions RAFTiNG.Tests/RAFTiNG.Tests.csproj
Expand Up @@ -59,6 +59,7 @@
</Compile>
<Compile Include="Acceptance\BasicRaftSteps.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="Unit\LogReplicationTest.cs" />
<Compile Include="Unit\PersistedStateTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Integration\RaftCommunication.feature.cs">
Expand Down
47 changes: 25 additions & 22 deletions RAFTiNG.Tests/Unit/BasicNodeTest.cs
Expand Up @@ -197,35 +197,38 @@ public void NodeWithMoreRescentTermDoesNotGrantVote()
[Test]
public void NodeStaysAFollowerWhenReceiveAppendEntries()
{
var settings = Helpers.BuildNodeSettings("1", new[] { "1", "2", "3", "4", "5" });
settings.TimeoutInMs = 10;
var node = new Node<string>(settings);

using (node)
using (Helpers.InitLog4Net())
{
var middleware = new Middleware();
node.SetMiddleware(middleware);
node.Initialize();
Thread.Sleep(30);
var settings = Helpers.BuildNodeSettings("1", new[] { "1", "2", "3", "4", "5" });
settings.TimeoutInMs = 10;
var node = new Node<string>(settings);

// should switch to candidate
Check.ThatEnum(node.Status).IsEqualTo(NodeStatus.Candidate);
using (node)
{
var middleware = new Middleware();
node.SetMiddleware(middleware);
node.Initialize();
Thread.Sleep(50);

// now we pretend there is a leader
var message = new AppendEntries<string>();
// should switch to candidate
Check.ThatEnum(node.Status).IsEqualTo(NodeStatus.Candidate);

message.LeaderId = "2";
message.LeaderTerm = 5;
message.PrevLogIndex = 0;
message.PrevLogTerm = 0;
var entry = new LogEntry<string>("dummy", 1L, 0);
message.Entries = new[] {entry};
middleware.SendMessage("1", message);
// now we pretend there is a leader
var message = new AppendEntries<string>();

Check.ThatEnum(node.Status).IsEqualTo(NodeStatus.Follower);
message.LeaderId = "2";
message.LeaderTerm = 5;
message.PrevLogIndex = -1;
message.PrevLogTerm = 0;
var entry = new LogEntry<string>("dummy", 1L);
message.Entries = new[] { entry };
middleware.SendMessage("1", message);

Check.That(node.State.LogEntries.Count).IsEqualTo(1);
Check.ThatEnum(node.Status).IsEqualTo(NodeStatus.Follower);

Check.That(node.State.LogEntries.Count).IsEqualTo(1);

}
}
}

Expand Down
60 changes: 60 additions & 0 deletions RAFTiNG.Tests/Unit/LogReplicationTest.cs
@@ -0,0 +1,60 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LogReplicationTest.cs" company="Cyrille DUPUYDAUBY">
// Copyright 2013 Cyrille DUPUYDAUBY
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace RAFTiNG.Tests.Unit
{
using System.Threading;

using NFluent;

using NUnit.Framework;

[TestFixture]
public class LogReplicationTest
{
[Test]
public void EmptyLogIsfilledTest()
{
using (Helpers.InitLog4Net())
{
var settings = Helpers.BuildNodeSettings("1", new[] { "1", "2" });
settings.TimeoutInMs = Timeout.Infinite;
var logReplicator = new LogReplicator<string>();
var middleware = new Middleware();

using (var node = new Node<string>(settings))
{
settings = Helpers.BuildNodeSettings("2", new[] { "1", "2" });
settings.TimeoutInMs = 1;
var leader = new Node<string>(settings);
leader.SetMiddleware(middleware);
node.SetMiddleware(middleware);
node.Initialize();
leader.Initialize();
Thread.Sleep(30);

// should stay follower
Check.ThatEnum(node.Status).IsEqualTo(NodeStatus.Follower);

Check.ThatEnum(leader.Status).IsEqualTo(NodeStatus.Leader);
}
}
}
}

public class LogReplicator<T>
{
}
}
10 changes: 7 additions & 3 deletions RAFTiNG.Tests/Unit/PersistedStateTests.cs
Expand Up @@ -47,9 +47,8 @@ public void CanAddCommands()
test.AddEntry(new LogEntry<string>("dummy"));
test.AddEntry(new LogEntry<string>("dummy"));

Check.That(test.LogEntries[0].Index).IsEqualTo(0);
Check.That(test.LogEntries[0].Term).IsEqualTo(0L);
Check.That(test.LogEntries[1].Term).IsEqualTo(0L);
Check.That(test.LogEntries[1].Index).IsEqualTo(1);
}

[Test]
Expand Down Expand Up @@ -85,7 +84,12 @@ public void LogEntryMatches()
[Test]
public void AppendEntries()
{

var test = new PersistedState<string> { CurrentTerm = 1 };

test.AddEntry(new LogEntry<string>("dummy"));
test.CurrentTerm = 2;
test.AppendEntries(-1, new[] {new LogEntry<string>("dummy", 2)});
Check.That(test.EntryMatches(0, 2L)).IsTrue();
}
}
}
1 change: 1 addition & 0 deletions RAFTiNG.sln
Expand Up @@ -8,6 +8,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7340327F-5CCF-4EFE-8E96-23957CCF92E6}"
ProjectSection(SolutionItems) = preProject
License.txt = License.txt
OpenQuestions.md = OpenQuestions.md
ReadMe.md = ReadMe.md
Specifications.pdf = Specifications.pdf
EndProjectSection
Expand Down
14 changes: 3 additions & 11 deletions RAFTiNG/LogEntry.cs
Expand Up @@ -37,13 +37,10 @@ public sealed class LogEntry<T>
/// </param>
/// <param name="term">Current term when command is stored.
/// </param>
/// <param name="index">Index for the current command.
/// </param>
public LogEntry(T command, long term, int index)
public LogEntry(T command, long term)
{
this.Command = command;
this.Term = term;
this.Index = index;
}

/// <summary>
Expand All @@ -69,11 +66,6 @@ public LogEntry(T command)
/// </summary>
public long Term { get; private set; }

/// <summary>
/// Gets the index for this command.
/// </summary>
public int Index { get; private set; }

#endregion

#region methods
Expand Down Expand Up @@ -115,7 +107,7 @@ public override int GetHashCode()
{
unchecked
{
return (this.Term.GetHashCode() * 397) ^ this.Index.GetHashCode();
return this.Term.GetHashCode();
}
}

Expand All @@ -130,7 +122,7 @@ public override int GetHashCode()
/// </returns>
private bool Equals(LogEntry<T> other)
{
return this.Term == other.Term && this.Index == other.Index;
return this.Term == other.Term;
}

#endregion
Expand Down
3 changes: 2 additions & 1 deletion RAFTiNG/Messages/AppendEntriesAck.cs
Expand Up @@ -19,7 +19,6 @@ namespace RAFTiNG.Messages
/// </summary>
public class AppendEntriesAck
{

private string nodeId;

private long term;
Expand Down Expand Up @@ -51,6 +50,7 @@ public long Term
{
return this.term;
}

set
{
this.term = value;
Expand All @@ -69,6 +69,7 @@ public bool Success
{
return this.success;
}

set
{
this.success = value;
Expand Down
15 changes: 8 additions & 7 deletions RAFTiNG/PersistedState.cs
Expand Up @@ -18,7 +18,6 @@

namespace RAFTiNG
{
using System;
using System.Collections.Generic;

/// <summary>
Expand Down Expand Up @@ -87,7 +86,7 @@ public int LastPersistedIndex
{
get
{
return this.LogEntries.Count > 0 ? this.LogEntries[this.LogEntries.Count - 1].Index : 0;
return this.LogEntries.Count > 0 ? this.LogEntries.Count - 1 : -1;
}
}

Expand All @@ -110,7 +109,7 @@ public void AddEntry(LogEntry<T> logEntry)
if (logEntry.Term == 0)
{
var newEntry = new LogEntry<T>(
logEntry.Command, this.CurrentTerm, this.LogEntries.Count);
logEntry.Command, this.CurrentTerm);
this.LogEntries.Add(newEntry);
}
else
Expand All @@ -125,7 +124,7 @@ public void AddEntry(LogEntry<T> logEntry)
/// <param name="command">The command.</param>
public void AddEntry(T command)
{
var newEntry = new LogEntry<T>(command, this.currentTerm, this.LogEntries.Count);
var newEntry = new LogEntry<T>(command, this.currentTerm);
this.LogEntries.Add(newEntry);
}

Expand Down Expand Up @@ -168,10 +167,11 @@ public bool LogIsBetterThan(long lastLogTerm, long lastLogIndex)
/// <returns>true if the entry at index <paramref name="index"/> has the term <paramref name="term"/>.</returns>
public bool EntryMatches(int index, long term)
{
if (index >= this.LogEntries.Count)
if (index >= this.LogEntries.Count || index < 0)
{
return false;
return index == -1;
}

return this.LogEntries[index].Term == term;
}

Expand All @@ -187,7 +187,7 @@ public void AppendEntries(int prevLogIndex, IEnumerable<LogEntry<T>> entries)
// nothing to add
return;
}

prevLogIndex++;
foreach (var logEntry in entries)
{
if (prevLogIndex == this.LogEntries.Count)
Expand All @@ -198,6 +198,7 @@ public void AppendEntries(int prevLogIndex, IEnumerable<LogEntry<T>> entries)
{
this.LogEntries[prevLogIndex] = logEntry;
}

prevLogIndex++;
}
}
Expand Down

0 comments on commit c55a3f1

Please sign in to comment.