Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions .config/mise/tasks/github-actions/resolve-comparison-refs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,29 @@ set -eu
: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}"
: "${EVENT_NAME:?EVENT_NAME is required}"

if [ "$EVENT_NAME" = pull_request ]; then
: "${BASE_SHA:?BASE_SHA is required for pull requests}"
: "${HEAD_SHA:?HEAD_SHA is required for pull requests}"
base=$BASE_SHA
head=$HEAD_SHA
else
: "${BEFORE_SHA:?BEFORE_SHA is required for push events}"
: "${CURRENT_SHA:?CURRENT_SHA is required for push events}"
base=$BEFORE_SHA
head=$CURRENT_SHA
fi
case "$EVENT_NAME" in
pull_request)
: "${BASE_SHA:?BASE_SHA is required for pull requests}"
: "${HEAD_SHA:?HEAD_SHA is required for pull requests}"
base=$BASE_SHA
head=$HEAD_SHA
;;
merge_group)
: "${MERGE_GROUP_BASE_SHA:?MERGE_GROUP_BASE_SHA is required for merge groups}"
: "${MERGE_GROUP_HEAD_SHA:?MERGE_GROUP_HEAD_SHA is required for merge groups}"
base=$MERGE_GROUP_BASE_SHA
head=$MERGE_GROUP_HEAD_SHA
;;
push)
: "${BEFORE_SHA:?BEFORE_SHA is required for push events}"
: "${CURRENT_SHA:?CURRENT_SHA is required for push events}"
base=$BEFORE_SHA
head=$CURRENT_SHA
;;
*)
echo "Unsupported event '$EVENT_NAME'." >&2
exit 1
;;
esac

printf 'base=%s\nhead=%s\n' "$base" "$head" >>"$GITHUB_OUTPUT"
3 changes: 3 additions & 0 deletions .github/workflows/build-container-ubuntu-lts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
- "docs/**"
- "samples/**"
- "**.md"
merge_group:
types:
- checks_requested
push:
branches:
- master
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/build-ubuntu-lts-arm64.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Ubuntu LTS ARM64

on:
pull_request:
paths-ignore:
- "docs/**"
- "samples/**"
- "**.md"
merge_group:
types:
- checks_requested
push:
branches:
- master
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/build-ubuntu-lts-x64.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
name: Ubuntu LTS X64

on:
pull_request:
paths-ignore:
- "docs/**"
- "samples/**"
- "**.md"
merge_group:
types:
- checks_requested
push:
branches:
- master
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
- "docs/**"
- "samples/**"
- "**.md"
merge_group:
types:
- checks_requested
push:
branches:
- master
Expand Down Expand Up @@ -73,6 +76,8 @@ jobs:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
MERGE_GROUP_BASE_SHA: ${{ github.event.merge_group.base_sha }}
MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }}
BEFORE_SHA: ${{ github.event.before }}
CURRENT_SHA: ${{ github.sha }}
run: mise run --skip-tools github-actions:resolve-comparison-refs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
Expand All @@ -24,23 +25,30 @@ public class grpc_request_forwarding_survives_leader_change<TLogFormat, TStreamI
{
private const string Stream = "$grpc-forwarding-failover";
private const string AuthorizationHeaderValue = "Basic YWRtaW46Y2hhbmdlaXQ=";
private static readonly TimeSpan ClusterTransitionTimeout = TimeSpan.FromMinutes(2);
private const int TestTimeoutMilliseconds = 5 * 60 * 1000;
private static readonly TimeSpan AuthenticationRetryDelay = TimeSpan.FromMilliseconds(100);
private static readonly TimeSpan RequestTimeout = TimeSpan.FromSeconds(30);
private static readonly TimeSpan ScenarioTimeout = TimeSpan.FromMinutes(4);

[Test]
[Timeout(TestTimeoutMilliseconds)]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
yordis marked this conversation as resolved.
public async Task completes_writes_through_a_surviving_follower_after_a_new_leader_is_elected()
{
var scenario = Stopwatch.StartNew();
AssertEx.IsOrBecomesTrue(
() =>
_nodes.Count(node => node.NodeState == VNodeState.Leader) == 1 &&
_nodes.Count(node => node.NodeState == VNodeState.Follower) == 2,
ClusterTransitionTimeout,
RemainingScenarioTime(scenario),
"The initial cluster topology did not stabilize",
MiniNodeLogging.WriteLogs);

var initialLeader = _nodes.Single(node => node.NodeState == VNodeState.Leader);
var initialFollowers = _nodes.Where(node => node.NodeState == VNodeState.Follower).ToArray();
Assert.That(await Append(initialFollowers[0].HttpEndPoint, ExpectedStreamRevision.NoStream), Is.EqualTo(0));
Assert.That(await Append(initialFollowers[1].HttpEndPoint, ExpectedStreamRevision.Exact(0)), Is.EqualTo(1));
Assert.That(await Append(initialFollowers[0].HttpEndPoint, ExpectedStreamRevision.NoStream, scenario),
Is.EqualTo(0));
Assert.That(await Append(initialFollowers[1].HttpEndPoint, ExpectedStreamRevision.Exact(0), scenario),
Is.EqualTo(1));

await initialLeader.Shutdown(keepDb: true);
_nodes[initialLeader.DebugIndex] = null;
Expand All @@ -49,16 +57,40 @@ public async Task completes_writes_through_a_surviving_follower_after_a_new_lead
() =>
_nodes.Count(node => node is not null && node.NodeState == VNodeState.Leader) == 1 &&
_nodes.Count(node => node is not null && node.NodeState == VNodeState.Follower) == 1,
ClusterTransitionTimeout,
RemainingScenarioTime(scenario),
"The surviving nodes did not elect a leader",
MiniNodeLogging.WriteLogs);

var forwardingFollower = _nodes.Single(node => node is not null && node.NodeState == VNodeState.Follower);
Assert.That(initialFollowers, Does.Contain(forwardingFollower));
Assert.That(await Append(forwardingFollower.HttpEndPoint, ExpectedStreamRevision.Exact(1)), Is.EqualTo(2));
Assert.That(await Append(forwardingFollower.HttpEndPoint, ExpectedStreamRevision.Exact(1), scenario),
Is.EqualTo(2));
}

private static async Task<ulong> Append(IPEndPoint endpoint, ExpectedStreamRevision expectedRevision)
private static async Task<ulong> Append(
IPEndPoint endpoint,
ExpectedStreamRevision expectedRevision,
Stopwatch scenario)
{
while (true)
{
try
{
return await AppendOnce(endpoint, expectedRevision, RemainingScenarioTime(scenario));
}
catch (RpcException ex) when (
ex.StatusCode is StatusCode.Unauthenticated or StatusCode.Unavailable &&
scenario.Elapsed < ScenarioTimeout)
{
await Task.Delay(AuthenticationRetryDelay);
}
}
}

private static async Task<ulong> AppendOnce(
IPEndPoint endpoint,
ExpectedStreamRevision expectedRevision,
TimeSpan remainingScenarioTime)
{
using var handler = new SocketsHttpHandler
{
Expand All @@ -78,7 +110,9 @@ private static async Task<ulong> Append(IPEndPoint endpoint, ExpectedStreamRevis
metadata.Add("authorization", AuthorizationHeaderValue);
return Task.CompletedTask;
}),
deadline: DateTime.UtcNow.AddSeconds(30)));
deadline: DateTime.UtcNow.Add(remainingScenarioTime < RequestTimeout
? remainingScenarioTime
: RequestTimeout)));

var options = new AppendReq.Types.Options
{
Expand Down Expand Up @@ -121,6 +155,14 @@ await call.RequestStream.WriteAsync(new AppendReq
return response.Success.CurrentRevision;
}

private static TimeSpan RemainingScenarioTime(Stopwatch scenario)
{
var remaining = ScenarioTimeout - scenario.Elapsed;
return remaining > TimeSpan.Zero
? remaining
: throw new TimeoutException("The forwarding failover scenario exceeded its time budget");
}

private enum ExpectedStreamRevisionKind
{
NoStream,
Expand Down
Loading