Skip to content

Commit 7cf592a

Browse files
committed
Add rate-limit draining application sample
1 parent d259c70 commit 7cf592a

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

TH-NETII Octokit Extensions.sln

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{675627EF-4
2323
EndProject
2424
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "THNETII.Octokit.Test", "test\THNETII.Octokit.Test\THNETII.Octokit.Test.csproj", "{69CA99F9-5112-4E8D-BBC9-5530EDAD5F71}"
2525
EndProject
26+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{E7F1EFF1-7525-4B6F-BD49-1051D7649096}"
27+
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "THNETII.Octokit.RateLimitDrain", "sample\THNETII.Octokit.RateLimitDrain\THNETII.Octokit.RateLimitDrain.csproj", "{F484C3B8-B039-41A8-8BAA-DB997A004618}"
29+
EndProject
2630
Global
2731
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2832
Debug|Any CPU = Debug|Any CPU
@@ -57,13 +61,26 @@ Global
5761
{69CA99F9-5112-4E8D-BBC9-5530EDAD5F71}.Release|x64.Build.0 = Release|Any CPU
5862
{69CA99F9-5112-4E8D-BBC9-5530EDAD5F71}.Release|x86.ActiveCfg = Release|Any CPU
5963
{69CA99F9-5112-4E8D-BBC9-5530EDAD5F71}.Release|x86.Build.0 = Release|Any CPU
64+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
65+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Debug|Any CPU.Build.0 = Debug|Any CPU
66+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Debug|x64.ActiveCfg = Debug|Any CPU
67+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Debug|x64.Build.0 = Debug|Any CPU
68+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Debug|x86.ActiveCfg = Debug|Any CPU
69+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Debug|x86.Build.0 = Debug|Any CPU
70+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Release|Any CPU.ActiveCfg = Release|Any CPU
71+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Release|Any CPU.Build.0 = Release|Any CPU
72+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Release|x64.ActiveCfg = Release|Any CPU
73+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Release|x64.Build.0 = Release|Any CPU
74+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Release|x86.ActiveCfg = Release|Any CPU
75+
{F484C3B8-B039-41A8-8BAA-DB997A004618}.Release|x86.Build.0 = Release|Any CPU
6076
EndGlobalSection
6177
GlobalSection(SolutionProperties) = preSolution
6278
HideSolutionNode = FALSE
6379
EndGlobalSection
6480
GlobalSection(NestedProjects) = preSolution
6581
{398AAFB6-5B6B-41F7-9FB3-9DAD64F71150} = {E0168284-A2AE-4A68-A586-1AA693585A53}
6682
{69CA99F9-5112-4E8D-BBC9-5530EDAD5F71} = {675627EF-4A75-4472-8770-70554C6DBDC1}
83+
{F484C3B8-B039-41A8-8BAA-DB997A004618} = {E7F1EFF1-7525-4B6F-BD49-1051D7649096}
6784
EndGlobalSection
6885
GlobalSection(ExtensibilityGlobals) = postSolution
6986
SolutionGuid = {3E0436D3-8146-43D8-AC88-AF842C217330}

http/github_v3_meta.http

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@apiUrl = https://api.github.com
2+
3+
###
4+
# @name GitHubApiMetadata
5+
HEAD {{apiUrl}}/meta
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.CommandLine;
3+
using System.CommandLine.Builder;
4+
using System.CommandLine.Hosting;
5+
using System.CommandLine.Invocation;
6+
using System.CommandLine.Parsing;
7+
using System.Linq;
8+
using System.Net.Http.Headers;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
12+
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.Extensions.Hosting;
14+
using Microsoft.Extensions.Logging;
15+
16+
using Octokit;
17+
using Octokit.DependencyInjection;
18+
19+
namespace THNETII.Octokit.RateLimitDrain
20+
{
21+
public static class Program
22+
{
23+
public static Task<int> Main(string[] args)
24+
{
25+
var cmdRoot = new RootCommand()
26+
{
27+
Handler = CommandHandler.Create
28+
(async (IHost host, CancellationToken cancelToken) =>
29+
{
30+
var provider = host.Services;
31+
var logger = provider.GetRequiredService<ILoggerFactory>()
32+
.CreateLogger(typeof(Program));
33+
var client = provider.GetRequiredService<IGitHubClient>();
34+
client.SetRequestTimeout(Timeout.InfiniteTimeSpan);
35+
36+
while (!cancelToken.IsCancellationRequested)
37+
{
38+
try
39+
{
40+
int remaining, limit;
41+
DateTimeOffset reset;
42+
43+
try
44+
{
45+
var meta = await client.Miscellaneous.GetMetadata().ConfigureAwait(false);
46+
var apiInfo = client.GetLastApiInfo();
47+
var rateLimitInfo = apiInfo.RateLimit;
48+
49+
remaining = rateLimitInfo.Remaining;
50+
limit = rateLimitInfo.Limit;
51+
reset = rateLimitInfo.Reset;
52+
53+
logger.LogInformation($"Received Response, rate limit info: {{{nameof(rateLimitInfo.Remaining)}}} / {{{nameof(rateLimitInfo.Limit)}}} messages remaining. Next Rate-Limit window starts at: {{{nameof(rateLimitInfo.Reset)}}}", remaining, limit, reset);
54+
}
55+
catch (RateLimitExceededException rateLimitExcept)
56+
{
57+
58+
foreach (var detail in rateLimitExcept.ApiError?.Errors ?? Enumerable.Empty<ApiErrorDetail>())
59+
{
60+
logger.LogError(new EventId(rateLimitExcept.HResult, detail.Code),
61+
detail.Message);
62+
}
63+
64+
remaining = rateLimitExcept.Remaining;
65+
limit = rateLimitExcept.Limit;
66+
reset = rateLimitExcept.Reset;
67+
68+
logger.LogError(new EventId(rateLimitExcept.HResult, nameof(RateLimitExceededException)),
69+
rateLimitExcept, $"Received Response, rate limit info: {{{nameof(rateLimitExcept.Remaining)}}} / {{{nameof(rateLimitExcept.Limit)}}} messages remaining. Next Rate-Limit window starts at: {{{nameof(rateLimitExcept.Reset)}}}",
70+
remaining, limit, reset.ToLocalTime());
71+
72+
var timeToReset = rateLimitExcept.Reset - DateTimeOffset.Now;
73+
if (timeToReset > TimeSpan.Zero)
74+
{
75+
var delayTask = Task.Delay(timeToReset, cancelToken);
76+
logger.LogInformation($"Time to wait until next Rate-Limit window: {{{nameof(HttpResponseHeaders.RetryAfter)}}}",
77+
timeToReset);
78+
await delayTask.ConfigureAwait(false);
79+
}
80+
else
81+
{
82+
logger.LogDebug("Time to next Rate-Limit windows is less than 0. Continuing with next request immediately.");
83+
}
84+
}
85+
}
86+
catch (OperationCanceledException except)
87+
{
88+
logger.LogWarning(except, "Stopping program due to intercepted cancellation");
89+
break;
90+
}
91+
}
92+
})
93+
};
94+
var cmdParser = new CommandLineBuilder(cmdRoot)
95+
.UseDefaults()
96+
.UseHost(Host.CreateDefaultBuilder, host =>
97+
{
98+
host.ConfigureServices(services =>
99+
{
100+
services.AddGitHubClient(nameof(Octokit), github => github
101+
.UseAssemblyProductHeader(typeof(Program).Assembly)
102+
.UseHttpClientFactoryConnection()
103+
.AddClients()
104+
);
105+
});
106+
})
107+
.Build();
108+
109+
return cmdParser.InvokeAsync(args);
110+
}
111+
}
112+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>netcoreapp3.1</TargetFramework>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.4" />
10+
<PackageReference Include="Octokit" Version="0.47.0" />
11+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20253.1" />
12+
<PackageReference Include="System.CommandLine.Hosting" Version="0.3.0-alpha.20253.1" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\src\THNETII.Octokit.DependencyInjection\THNETII.Octokit.DependencyInjection.csproj" />
17+
</ItemGroup>
18+
19+
</Project>

0 commit comments

Comments
 (0)