Skip to content

Commit

Permalink
Reduce Helix JSON response deserialization times by leveraging STJ so…
Browse files Browse the repository at this point in the history
…urcegen

Request body serialization improvements are blocked by dotnet/runtime#60378 and dotnet/runtime#51544
  • Loading branch information
ErisApps committed Oct 16, 2021
1 parent 0312051 commit 0bfdb69
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 19 deletions.
20 changes: 20 additions & 0 deletions CatCore/Helpers/JSON/TwitchHelixSerializerContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Text.Json.Serialization;
using CatCore.Models.Twitch.Helix.Responses;
using CatCore.Models.Twitch.Helix.Responses.Bits.Cheermotes;
using CatCore.Models.Twitch.Helix.Responses.Polls;
using CatCore.Models.Twitch.Helix.Responses.Predictions;

namespace CatCore.Helpers.JSON
{
[JsonSerializable(typeof(ResponseBase<UserData>))]
[JsonSerializable(typeof(ResponseBase<CreateStreamMarkerData>))]
[JsonSerializable(typeof(ResponseBaseWithPagination<ChannelData>))]
[JsonSerializable(typeof(ResponseBase<PollData>))]
[JsonSerializable(typeof(ResponseBaseWithPagination<PollData>))]
[JsonSerializable(typeof(ResponseBase<PredictionData>))]
[JsonSerializable(typeof(ResponseBaseWithPagination<PredictionData>))]
[JsonSerializable(typeof(ResponseBase<CheermoteGroupData>))]
internal partial class TwitchHelixSerializerContext : JsonSerializerContext
{
}
}
21 changes: 11 additions & 10 deletions CatCore/Services/Twitch/TwitchHelixApiService.Calls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CatCore.Helpers.JSON;
using CatCore.Models.Twitch.Helix.Requests;
using CatCore.Models.Twitch.Helix.Requests.Polls;
using CatCore.Models.Twitch.Helix.Requests.Predictions;
Expand Down Expand Up @@ -62,7 +63,7 @@ void CheckCount(ref string[]? array, out bool hasBool)
urlBuilder.Append("login=").Append(string.Join("&login=", loginNames!));
}

return GetAsync<ResponseBase<UserData>>(urlBuilder.ToString(), cancellationToken);
return GetAsync(urlBuilder.ToString(), TwitchHelixSerializerContext.Default.ResponseBaseUserData, cancellationToken);
}

/// <inheritdoc />
Expand All @@ -74,7 +75,7 @@ void CheckCount(ref string[]? array, out bool hasBool)
}

var body = new CreateStreamMarkerRequestDto(userId, description);
return PostAsync<ResponseBase<CreateStreamMarkerData>, CreateStreamMarkerRequestDto>($"{TWITCH_HELIX_BASEURL}streams/markers", body, cancellationToken);
return PostAsync($"{TWITCH_HELIX_BASEURL}streams/markers", body, TwitchHelixSerializerContext.Default.ResponseBaseCreateStreamMarkerData, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -112,7 +113,7 @@ void CheckCount(ref string[]? array, out bool hasBool)
urlBuilder.Append($"&after={continuationCursor}");
}

return GetAsync<ResponseBaseWithPagination<ChannelData>>(urlBuilder.ToString(), cancellationToken);
return GetAsync(urlBuilder.ToString(), TwitchHelixSerializerContext.Default.ResponseBaseWithPaginationChannelData, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -159,7 +160,7 @@ void CheckCount(ref string[]? array, out bool hasBool)
urlBuilder.Append($"&after={continuationCursor}");
}

return GetAsync<ResponseBaseWithPagination<PollData>>(urlBuilder.ToString(), cancellationToken);
return GetAsync(urlBuilder.ToString(), TwitchHelixSerializerContext.Default.ResponseBaseWithPaginationPollData, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -237,7 +238,7 @@ void OptionalParametersValidation(ref bool? voteEnabled, ref uint? costPerVote,
OptionalParametersValidation(ref channelPointsVotingEnabled, ref channelPointsPerVote, 1000000);

var body = new CreatePollRequestDto(userId, title, pollChoices, duration, bitsVotingEnabled, bitsPerVote, channelPointsVotingEnabled, channelPointsPerVote);
return PostAsync<ResponseBase<PollData>, CreatePollRequestDto>($"{TWITCH_HELIX_BASEURL}polls", body, cancellationToken);
return PostAsync($"{TWITCH_HELIX_BASEURL}polls", body, TwitchHelixSerializerContext.Default.ResponseBasePollData, cancellationToken);
}

/// <inheritdoc />
Expand All @@ -262,7 +263,7 @@ void OptionalParametersValidation(ref bool? voteEnabled, ref uint? costPerVote,
}

var body = new EndPollRequestDto(userId, pollId, pollStatus);
return PatchAsync<ResponseBase<PollData>, EndPollRequestDto>($"{TWITCH_HELIX_BASEURL}polls", body, cancellationToken);
return PatchAsync($"{TWITCH_HELIX_BASEURL}polls", body, TwitchHelixSerializerContext.Default.ResponseBasePollData, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -310,7 +311,7 @@ void OptionalParametersValidation(ref bool? voteEnabled, ref uint? costPerVote,
urlBuilder.Append($"&after={continuationCursor}");
}

return GetAsync<ResponseBaseWithPagination<PredictionData>>(urlBuilder.ToString(), cancellationToken);
return GetAsync(urlBuilder.ToString(), TwitchHelixSerializerContext.Default.ResponseBaseWithPaginationPredictionData, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -355,7 +356,7 @@ void OptionalParametersValidation(ref bool? voteEnabled, ref uint? costPerVote,
}

var body = new CreatePredictionsRequestDto(userId, title, predictionOutcomes, duration);
return PostAsync<ResponseBase<PredictionData>, CreatePredictionsRequestDto>($"{TWITCH_HELIX_BASEURL}predictions", body, cancellationToken);
return PostAsync($"{TWITCH_HELIX_BASEURL}predictions", body, TwitchHelixSerializerContext.Default.ResponseBasePredictionData, cancellationToken);
}

/// <inheritdoc />
Expand Down Expand Up @@ -385,7 +386,7 @@ void OptionalParametersValidation(ref bool? voteEnabled, ref uint? costPerVote,
}

var body = new EndPredictionRequestDto(userId, predictionId, predictionStatus, winningOutcomeId);
return PatchAsync<ResponseBase<PredictionData>, EndPredictionRequestDto>($"{TWITCH_HELIX_BASEURL}predictions", body, cancellationToken);
return PatchAsync($"{TWITCH_HELIX_BASEURL}predictions", body, TwitchHelixSerializerContext.Default.ResponseBasePredictionData, cancellationToken);
}

/// <inheritdoc />
Expand All @@ -397,7 +398,7 @@ void OptionalParametersValidation(ref bool? voteEnabled, ref uint? costPerVote,
urlBuilder.Append("?broadcaster_id=").Append(userId);
}

return GetAsync<ResponseBase<CheermoteGroupData>>(urlBuilder.ToString(), cancellationToken);
return GetAsync(urlBuilder.ToString(), TwitchHelixSerializerContext.Default.ResponseBaseCheermoteGroupData, cancellationToken);
}
}
}
19 changes: 10 additions & 9 deletions CatCore/Services/Twitch/TwitchHelixApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Net.Http;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization.Metadata;
using System.Threading;
using System.Threading.Tasks;
using CatCore.Helpers;
Expand Down Expand Up @@ -69,7 +70,7 @@ internal TwitchHelixApiService(ILogger logger, ITwitchAuthService twitchAuthServ
_combinedHelixPolicy = Policy.WrapAsync(bulkheadPolicy, enhanceYourCalmPolicy, reAuthPolicy);
}

private async Task<TResponse?> GetAsync<TResponse>(string url, CancellationToken? cancellationToken = null) where TResponse : struct
private async Task<TResponse?> GetAsync<TResponse>(string url, JsonTypeInfo<TResponse> jsonResponseTypeInfo, CancellationToken? cancellationToken = null) where TResponse : struct
{
#if DEBUG
if (string.IsNullOrWhiteSpace(url))
Expand Down Expand Up @@ -97,25 +98,25 @@ internal TwitchHelixApiService(ILogger logger, ITwitchAuthService twitchAuthServ
return null;
}

return await httpResponseMessage.Content.ReadFromJsonAsync<TResponse>(options: null, cancellationToken ?? default).ConfigureAwait(false);
return await httpResponseMessage.Content.ReadFromJsonAsync(jsonResponseTypeInfo, cancellationToken ?? default).ConfigureAwait(false);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Task<TResponse?> PostAsync<TResponse, TBody>(string url, TBody body, CancellationToken? cancellationToken = null) where TResponse : struct =>
CallEndpointWithBodyExpectBody<TResponse, TBody>(HttpMethod.Post, url, body, cancellationToken);
private Task<TResponse?> PostAsync<TResponse, TBody>(string url, TBody body, JsonTypeInfo<TResponse> jsonResponseTypeInfo, CancellationToken? cancellationToken = null)
where TResponse : struct => CallEndpointWithBodyExpectBody(HttpMethod.Post, url, body, jsonResponseTypeInfo, cancellationToken);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Task<bool> PostAsync<TBody>(string url, TBody body, CancellationToken? cancellationToken = null) => CallEndpointWithBodyNoBody(HttpMethod.Post, url, body, cancellationToken);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Task<TResponse?> PatchAsync<TResponse, TBody>(string url, TBody body, CancellationToken? cancellationToken = null) where TResponse : struct =>
CallEndpointWithBodyExpectBody<TResponse, TBody>(HttpMethodPatch, url, body, cancellationToken);
private Task<TResponse?> PatchAsync<TResponse, TBody>(string url, TBody body, JsonTypeInfo<TResponse> jsonResponseTypeInfo, CancellationToken? cancellationToken = null)
where TResponse : struct => CallEndpointWithBodyExpectBody(HttpMethodPatch, url, body, jsonResponseTypeInfo, cancellationToken);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private Task<bool> PatchAsync<TBody>(string url, TBody body, CancellationToken? cancellationToken = null) => CallEndpointWithBodyNoBody(HttpMethodPatch, url, body, cancellationToken);

private async Task<TResponse?> CallEndpointWithBodyExpectBody<TResponse, TBody>(HttpMethod httpMethod, string url, TBody body, CancellationToken? cancellationToken = null)
where TResponse : struct
private async Task<TResponse?> CallEndpointWithBodyExpectBody<TResponse, TBody>(HttpMethod httpMethod, string url, TBody body, JsonTypeInfo<TResponse> jsonResponseTypeInfo,
CancellationToken? cancellationToken = null) where TResponse : struct
{
#if DEBUG
if (string.IsNullOrWhiteSpace(url))
Expand Down Expand Up @@ -152,7 +153,7 @@ internal TwitchHelixApiService(ILogger logger, ITwitchAuthService twitchAuthServ
return null;
}

return await httpResponseMessage.Content.ReadFromJsonAsync<TResponse>(options: null, cancellationToken ?? default).ConfigureAwait(false);
return await httpResponseMessage.Content.ReadFromJsonAsync(jsonResponseTypeInfo, cancellationToken ?? default).ConfigureAwait(false);
}

private async Task<bool> CallEndpointWithBodyNoBody<TBody>(HttpMethod httpMethod, string url, TBody body, CancellationToken? cancellationToken = null)
Expand Down

0 comments on commit 0bfdb69

Please sign in to comment.