feat(server): adds minimum keepalive and headroom, chore: updates packages to fix vulnerabilities
Build & Deploy / build (push) Failing after 1m45s
Build & Deploy / build (push) Failing after 1m45s
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="10.0.10" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
[assembly: InternalsVisibleTo("Ngino.Client.Tests")]
|
[assembly: InternalsVisibleTo("Ngino.Client.Tests")]
|
||||||
|
[assembly: InternalsVisibleTo("ReverseLlama.Client.Tests")]
|
||||||
|
|||||||
@@ -369,7 +369,14 @@ internal static class AdminEndpoints
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var member = store.AddGroupClient(id, request.ClientId, request.Model, request.ClientPattern);
|
var member = store.AddGroupClient(
|
||||||
|
id,
|
||||||
|
request.ClientId,
|
||||||
|
request.Model,
|
||||||
|
request.ClientPattern,
|
||||||
|
request.KeepaliveInstancesToKeepAlive,
|
||||||
|
request.KeepaliveMaxParallelismPerClient,
|
||||||
|
request.KeepaliveParallelismHeadroom);
|
||||||
return Results.Json(member);
|
return Results.Json(member);
|
||||||
}
|
}
|
||||||
catch (ArgumentException exception)
|
catch (ArgumentException exception)
|
||||||
@@ -889,7 +896,10 @@ internal sealed record UpdateGroupRequest(string Name);
|
|||||||
internal sealed record AddGroupClientRequest(
|
internal sealed record AddGroupClientRequest(
|
||||||
string? ClientId,
|
string? ClientId,
|
||||||
string? Model,
|
string? Model,
|
||||||
string? ClientPattern);
|
string? ClientPattern,
|
||||||
|
int? KeepaliveInstancesToKeepAlive,
|
||||||
|
int? KeepaliveMaxParallelismPerClient,
|
||||||
|
int? KeepaliveParallelismHeadroom);
|
||||||
|
|
||||||
internal sealed record SetUserKeyGroupsRequest(IReadOnlyList<string>? GroupIds);
|
internal sealed record SetUserKeyGroupsRequest(IReadOnlyList<string>? GroupIds);
|
||||||
|
|
||||||
|
|||||||
@@ -824,7 +824,10 @@ internal sealed class ManagementStore
|
|||||||
using var connection = OpenConnection();
|
using var connection = OpenConnection();
|
||||||
using var command = connection.CreateCommand();
|
using var command = connection.CreateCommand();
|
||||||
command.CommandText = """
|
command.CommandText = """
|
||||||
SELECT id, group_id, client_id, model, client_pattern
|
SELECT id, group_id, client_id, model, client_pattern,
|
||||||
|
keepalive_instances_to_keep_alive,
|
||||||
|
keepalive_max_parallelism_per_client,
|
||||||
|
keepalive_parallelism_headroom
|
||||||
FROM group_members
|
FROM group_members
|
||||||
WHERE group_id = $group_id
|
WHERE group_id = $group_id
|
||||||
ORDER BY client_id, model, client_pattern
|
ORDER BY client_id, model, client_pattern
|
||||||
@@ -840,14 +843,22 @@ internal sealed class ManagementStore
|
|||||||
reader.GetString(1),
|
reader.GetString(1),
|
||||||
reader.IsDBNull(2) ? null : reader.GetString(2),
|
reader.IsDBNull(2) ? null : reader.GetString(2),
|
||||||
reader.IsDBNull(3) ? null : reader.GetString(3),
|
reader.IsDBNull(3) ? null : reader.GetString(3),
|
||||||
reader.IsDBNull(4) ? null : reader.GetString(4)));
|
reader.IsDBNull(4) ? null : reader.GetString(4),
|
||||||
|
ReadKeepalivePolicy(reader, 5, 6, 7)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupClientInfo AddGroupClient(string groupId, string? clientId, string? model, string? clientPattern)
|
public GroupClientInfo AddGroupClient(
|
||||||
|
string groupId,
|
||||||
|
string? clientId,
|
||||||
|
string? model,
|
||||||
|
string? clientPattern,
|
||||||
|
int? keepaliveInstancesToKeepAlive,
|
||||||
|
int? keepaliveMaxParallelismPerClient,
|
||||||
|
int? keepaliveParallelismHeadroom)
|
||||||
{
|
{
|
||||||
EnsureAvailable();
|
EnsureAvailable();
|
||||||
|
|
||||||
@@ -873,24 +884,46 @@ internal sealed class ManagementStore
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var policy = NormalizeKeepalivePolicy(
|
||||||
|
keepaliveInstancesToKeepAlive,
|
||||||
|
keepaliveMaxParallelismPerClient,
|
||||||
|
keepaliveParallelismHeadroom);
|
||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
using var connection = OpenConnection();
|
using var connection = OpenConnection();
|
||||||
using var command = connection.CreateCommand();
|
using var command = connection.CreateCommand();
|
||||||
command.CommandText = """
|
command.CommandText = """
|
||||||
INSERT INTO group_members (group_id, client_id, model, client_pattern)
|
INSERT INTO group_members (
|
||||||
VALUES ($group_id, $client_id, $model, $client_pattern)
|
group_id,
|
||||||
|
client_id,
|
||||||
|
model,
|
||||||
|
client_pattern,
|
||||||
|
keepalive_instances_to_keep_alive,
|
||||||
|
keepalive_max_parallelism_per_client,
|
||||||
|
keepalive_parallelism_headroom)
|
||||||
|
VALUES (
|
||||||
|
$group_id,
|
||||||
|
$client_id,
|
||||||
|
$model,
|
||||||
|
$client_pattern,
|
||||||
|
$keepalive_instances_to_keep_alive,
|
||||||
|
$keepalive_max_parallelism_per_client,
|
||||||
|
$keepalive_parallelism_headroom)
|
||||||
""";
|
""";
|
||||||
command.Parameters.AddWithValue("$group_id", groupId);
|
command.Parameters.AddWithValue("$group_id", groupId);
|
||||||
command.Parameters.AddWithValue("$client_id", string.IsNullOrWhiteSpace(clientId) ? DBNull.Value : clientId);
|
command.Parameters.AddWithValue("$client_id", string.IsNullOrWhiteSpace(clientId) ? DBNull.Value : clientId);
|
||||||
command.Parameters.AddWithValue("$model", string.IsNullOrWhiteSpace(model) ? DBNull.Value : model);
|
command.Parameters.AddWithValue("$model", string.IsNullOrWhiteSpace(model) ? DBNull.Value : model);
|
||||||
command.Parameters.AddWithValue("$client_pattern", string.IsNullOrWhiteSpace(clientPattern) ? DBNull.Value : clientPattern);
|
command.Parameters.AddWithValue("$client_pattern", string.IsNullOrWhiteSpace(clientPattern) ? DBNull.Value : clientPattern);
|
||||||
|
command.Parameters.AddWithValue("$keepalive_instances_to_keep_alive", policy.InstancesToKeepAlive);
|
||||||
|
command.Parameters.AddWithValue("$keepalive_max_parallelism_per_client", policy.MaxParallelismPerClient);
|
||||||
|
command.Parameters.AddWithValue("$keepalive_parallelism_headroom", policy.ParallelismHeadroom);
|
||||||
command.ExecuteNonQuery();
|
command.ExecuteNonQuery();
|
||||||
|
|
||||||
using var idCommand = connection.CreateCommand();
|
using var idCommand = connection.CreateCommand();
|
||||||
idCommand.CommandText = "SELECT last_insert_rowid()";
|
idCommand.CommandText = "SELECT last_insert_rowid()";
|
||||||
var insertedId = (long)idCommand.ExecuteScalar()!;
|
var insertedId = (long)idCommand.ExecuteScalar()!;
|
||||||
return new GroupClientInfo(insertedId, groupId, clientId, model, clientPattern);
|
return new GroupClientInfo(insertedId, groupId, clientId, model, clientPattern, policy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1987,7 +2020,10 @@ internal sealed class ManagementStore
|
|||||||
group_id TEXT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
group_id TEXT NOT NULL REFERENCES groups(id) ON DELETE CASCADE,
|
||||||
client_id TEXT NULL,
|
client_id TEXT NULL,
|
||||||
model TEXT NULL,
|
model TEXT NULL,
|
||||||
client_pattern TEXT NULL
|
client_pattern TEXT NULL,
|
||||||
|
keepalive_instances_to_keep_alive INTEGER NOT NULL DEFAULT 1,
|
||||||
|
keepalive_max_parallelism_per_client INTEGER NOT NULL DEFAULT 1,
|
||||||
|
keepalive_parallelism_headroom INTEGER NOT NULL DEFAULT 1
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_group_members_group_id
|
CREATE INDEX IF NOT EXISTS idx_group_members_group_id
|
||||||
@@ -2010,6 +2046,29 @@ internal sealed class ManagementStore
|
|||||||
""";
|
""";
|
||||||
command.ExecuteNonQuery();
|
command.ExecuteNonQuery();
|
||||||
|
|
||||||
|
using (var migrate = connection.CreateCommand())
|
||||||
|
{
|
||||||
|
migrate.CommandText = """
|
||||||
|
SELECT COUNT(*) FROM pragma_table_info('group_members') WHERE name = 'keepalive_instances_to_keep_alive'
|
||||||
|
""";
|
||||||
|
var hasKeepaliveInstancesColumn = (long)migrate.ExecuteScalar()! > 0;
|
||||||
|
|
||||||
|
if (!hasKeepaliveInstancesColumn)
|
||||||
|
{
|
||||||
|
using var addKeepaliveInstances = connection.CreateCommand();
|
||||||
|
addKeepaliveInstances.CommandText = "ALTER TABLE group_members ADD COLUMN keepalive_instances_to_keep_alive INTEGER NOT NULL DEFAULT 1";
|
||||||
|
addKeepaliveInstances.ExecuteNonQuery();
|
||||||
|
|
||||||
|
using var addKeepaliveMaxParallelism = connection.CreateCommand();
|
||||||
|
addKeepaliveMaxParallelism.CommandText = "ALTER TABLE group_members ADD COLUMN keepalive_max_parallelism_per_client INTEGER NOT NULL DEFAULT 1";
|
||||||
|
addKeepaliveMaxParallelism.ExecuteNonQuery();
|
||||||
|
|
||||||
|
using var addKeepaliveHeadroom = connection.CreateCommand();
|
||||||
|
addKeepaliveHeadroom.CommandText = "ALTER TABLE group_members ADD COLUMN keepalive_parallelism_headroom INTEGER NOT NULL DEFAULT 1";
|
||||||
|
addKeepaliveHeadroom.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
using (var migrate = connection.CreateCommand())
|
using (var migrate = connection.CreateCommand())
|
||||||
{
|
{
|
||||||
migrate.CommandText = """
|
migrate.CommandText = """
|
||||||
@@ -2231,6 +2290,30 @@ internal sealed class ManagementStore
|
|||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static GroupClientKeepalivePolicy? ReadKeepalivePolicy(SqliteDataReader reader, int instancesOrdinal, int maxParallelismOrdinal, int headroomOrdinal)
|
||||||
|
{
|
||||||
|
if (reader.IsDBNull(instancesOrdinal) || reader.IsDBNull(maxParallelismOrdinal) || reader.IsDBNull(headroomOrdinal))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NormalizeKeepalivePolicy(
|
||||||
|
reader.GetInt32(instancesOrdinal),
|
||||||
|
reader.GetInt32(maxParallelismOrdinal),
|
||||||
|
reader.GetInt32(headroomOrdinal));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GroupClientKeepalivePolicy NormalizeKeepalivePolicy(
|
||||||
|
int? instancesToKeepAlive,
|
||||||
|
int? maxParallelismPerClient,
|
||||||
|
int? parallelismHeadroom)
|
||||||
|
{
|
||||||
|
return new GroupClientKeepalivePolicy(
|
||||||
|
Math.Max(1, instancesToKeepAlive ?? GroupClientKeepalivePolicy.Default.InstancesToKeepAlive),
|
||||||
|
Math.Max(1, maxParallelismPerClient ?? GroupClientKeepalivePolicy.Default.MaxParallelismPerClient),
|
||||||
|
Math.Max(1, parallelismHeadroom ?? GroupClientKeepalivePolicy.Default.ParallelismHeadroom));
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureAvailable()
|
private void EnsureAvailable()
|
||||||
{
|
{
|
||||||
if (!_isAvailable)
|
if (!_isAvailable)
|
||||||
@@ -2420,7 +2503,16 @@ internal sealed record GroupClientInfo(
|
|||||||
string GroupId,
|
string GroupId,
|
||||||
string? ClientId,
|
string? ClientId,
|
||||||
string? Model,
|
string? Model,
|
||||||
string? ClientPattern);
|
string? ClientPattern,
|
||||||
|
GroupClientKeepalivePolicy? KeepalivePolicy = null);
|
||||||
|
|
||||||
|
internal sealed record GroupClientKeepalivePolicy(
|
||||||
|
int InstancesToKeepAlive,
|
||||||
|
int MaxParallelismPerClient,
|
||||||
|
int ParallelismHeadroom)
|
||||||
|
{
|
||||||
|
public static GroupClientKeepalivePolicy Default { get; } = new(1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
internal sealed record UserKeyGroupInfo(
|
internal sealed record UserKeyGroupInfo(
|
||||||
string UserKeyId,
|
string UserKeyId,
|
||||||
|
|||||||
@@ -3,14 +3,20 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ElmahCore" Version="2.1.2" />
|
<PackageReference Include="ElmahCore" Version="2.1.2" />
|
||||||
<PackageReference Include="ElmahCore.MySql" Version="2.1.2" />
|
<PackageReference Include="ElmahCore.MySql" Version="2.1.2" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.28" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.10" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.10" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.28" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.10" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.10" />
|
||||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.3" />
|
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
||||||
<PackageReference Include="System.Text.Encodings.Web" Version="8.0.0" />
|
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Data.SqlClient" Version="4.9.1" />
|
||||||
|
<PackageReference Include="System.Drawing.Common" Version="9.0.0" />
|
||||||
|
<PackageReference Include="System.Text.Encodings.Web" Version="10.0.10" />
|
||||||
|
<PackageReference Include="System.Text.Json" Version="10.0.10" />
|
||||||
<ProjectReference Include="..\Ngino.Protocol\Ngino.Protocol.csproj" />
|
<ProjectReference Include="..\Ngino.Protocol\Ngino.Protocol.csproj" />
|
||||||
|
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||||
|
<_Parameter1>Ngino.Client.Tests</_Parameter1>
|
||||||
|
</AssemblyAttribute>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("ReverseLlama.Client.Tests")]
|
||||||
@@ -228,6 +228,9 @@ content.addEventListener("submit", async (event) => {
|
|||||||
if (data.clientPattern && data.clientPattern.trim()) {
|
if (data.clientPattern && data.clientPattern.trim()) {
|
||||||
body.clientPattern = data.clientPattern.trim();
|
body.clientPattern = data.clientPattern.trim();
|
||||||
}
|
}
|
||||||
|
body.keepaliveInstancesToKeepAlive = parseInt(data.keepaliveInstancesToKeepAlive, 10) || 1;
|
||||||
|
body.keepaliveMaxParallelismPerClient = parseInt(data.keepaliveMaxParallelismPerClient, 10) || 1;
|
||||||
|
body.keepaliveParallelismHeadroom = parseInt(data.keepaliveParallelismHeadroom, 10) || 1;
|
||||||
|
|
||||||
if (!body.clientId && !body.clientPattern) {
|
if (!body.clientId && !body.clientPattern) {
|
||||||
throw new Error("Either Client ID or Client pattern is required.");
|
throw new Error("Either Client ID or Client pattern is required.");
|
||||||
@@ -981,9 +984,21 @@ function renderGroupDetail() {
|
|||||||
<label for="addClientPattern">Client pattern (regex, optional)</label>
|
<label for="addClientPattern">Client pattern (regex, optional)</label>
|
||||||
<input class="input" id="addClientPattern" name="clientPattern" placeholder="GPU_[0-9]*">
|
<input class="input" id="addClientPattern" name="clientPattern" placeholder="GPU_[0-9]*">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="addClientKeepaliveInstances">Keepalive instances</label>
|
||||||
|
<input class="input" id="addClientKeepaliveInstances" name="keepaliveInstancesToKeepAlive" type="number" min="1" step="1" value="1">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="addClientKeepaliveMaxParallelism">Max parallelism per client</label>
|
||||||
|
<input class="input" id="addClientKeepaliveMaxParallelism" name="keepaliveMaxParallelismPerClient" type="number" min="1" step="1" value="1">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="addClientKeepaliveHeadroom">Parallelism headroom</label>
|
||||||
|
<input class="input" id="addClientKeepaliveHeadroom" name="keepaliveParallelismHeadroom" type="number" min="1" step="1" value="1">
|
||||||
|
</div>
|
||||||
<button class="button" type="submit">Add</button>
|
<button class="button" type="submit">Add</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="cell-sub" style="margin-top:8px">Provide either a Client ID (for explicit client access) or a Client pattern (for regex-based matching). Model is optional to restrict to a specific model.</div>
|
<div class="cell-sub" style="margin-top:8px">Provide either a Client ID (for explicit client access) or a Client pattern (for regex-based matching). Model is optional to restrict to a specific model. Keepalive defaults to 1 instance, 1 parallelism, and 1 headroom.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
@@ -1096,6 +1111,17 @@ function renderGroupDetail() {
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatKeepalivePolicy(policy) {
|
||||||
|
if (!policy) {
|
||||||
|
return `<div class="cell-sub">Default (1 / 1 / 1)</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="cell-main">${policy.instancesToKeepAlive} instance${policy.instancesToKeepAlive === 1 ? "" : "s"}</div>
|
||||||
|
<div class="cell-sub">Parallelism ${policy.maxParallelismPerClient} · headroom ${policy.parallelismHeadroom}</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
function groupClientsTable(clients, groupId) {
|
function groupClientsTable(clients, groupId) {
|
||||||
const rows = clients.map((client) => `
|
const rows = clients.map((client) => `
|
||||||
<tr>
|
<tr>
|
||||||
@@ -1117,6 +1143,7 @@ function groupClientsTable(clients, groupId) {
|
|||||||
: `<div class="cell-sub">-</div>`
|
: `<div class="cell-sub">-</div>`
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
|
<td>${formatKeepalivePolicy(client.keepalivePolicy)}</td>
|
||||||
<td>
|
<td>
|
||||||
<button class="button danger" data-action="remove-client" data-group-id="${escapeAttr(groupId)}" data-member-id="${client.id}">Remove</button>
|
<button class="button danger" data-action="remove-client" data-group-id="${escapeAttr(groupId)}" data-member-id="${client.id}">Remove</button>
|
||||||
</td>
|
</td>
|
||||||
@@ -1130,6 +1157,7 @@ function groupClientsTable(clients, groupId) {
|
|||||||
<th>Client ID</th>
|
<th>Client ID</th>
|
||||||
<th>Model</th>
|
<th>Model</th>
|
||||||
<th>Client pattern</th>
|
<th>Client pattern</th>
|
||||||
|
<th>Keepalive policy</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using Ngino.Server;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace ReverseLlama.Client.Tests;
|
||||||
|
|
||||||
|
public class GroupKeepalivePolicyTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void KeepalivePolicy_IsRoundTripped_ThroughGroupClientInfo()
|
||||||
|
{
|
||||||
|
var policy = new GroupClientKeepalivePolicy(2, 3, 4);
|
||||||
|
var info = new GroupClientInfo(1, "group-1", "client-1", "model-a", "pattern", policy);
|
||||||
|
|
||||||
|
Assert.Equal(2, info.KeepalivePolicy?.InstancesToKeepAlive);
|
||||||
|
Assert.Equal(3, info.KeepalivePolicy?.MaxParallelismPerClient);
|
||||||
|
Assert.Equal(4, info.KeepalivePolicy?.ParallelismHeadroom);
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-3
@@ -9,9 +9,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
|
||||||
<PackageReference Include="xunit" Version="2.9.2" />
|
<PackageReference Include="xunit" Version="2.9.3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Ngino.Client\Ngino.Client.csproj" />
|
<ProjectReference Include="..\..\src\Ngino.Client\Ngino.Client.csproj" />
|
||||||
|
<ProjectReference Include="..\..\src\Ngino.Server\Ngino.Server.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
Reference in New Issue
Block a user