Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Faster csv download#44

Merged
shoe116 merged 8 commits intomasterfrom
faster_csv_download
Nov 3, 2021
Merged

Faster csv download#44
shoe116 merged 8 commits intomasterfrom
faster_csv_download

Conversation

@shoe116
Copy link
Copy Markdown
Contributor

@shoe116 shoe116 commented Nov 1, 2021

to close: #34
非同期処理の制御がうまくいっていなかったために deadlock が起こってしまっていたっぽい

  • 複雑化していた非同期処理を ApiClientCore のみで行うようにする & await の設定をキチンと引き継ぐ
  • 開発者設定から並列実行数の設定を削除
    image

Copy link
Copy Markdown
Contributor Author

@shoe116 shoe116 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akiomik これで UDF も CSV DLもサクサク動くようになります

}
}

public void Dispose()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constructor で httpClient を作ってるので、せっかくなので IDisposable にします

Comment thread BuffettCodeAPIClient/ApiClientCore.cs Outdated
/// </summary>

public class ApiClientCore
public class ApiClientCore : IDisposable
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こいつだけ非同期で動きます

return response;
else
{
return apiClientCore.Get(request, isConfigureAwait).Result;
Copy link
Copy Markdown
Contributor Author

@shoe116 shoe116 Nov 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この時点で取得を待って動くようにします。当然ながら cache からは常に string が直接返ってくるので、こっちのが全体として違和感がないと思う。

}

public async Task<JObject> GetQuarter(string ticker, FiscalQuarterPeriod period, bool useOndemand, bool isConfigureAwait = true, bool useCache = true)
public JObject GetQuarter(string ticker, FiscalQuarterPeriod period, bool useOndemand, bool isConfigureAwait = true, bool useCache = true)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApiClientCoreWithCache が同期的に動くようになったので、ここも Json をそのまま返します

}

private void textAPIKey_TextChanged(object sender, EventArgs e)
private void TextAPIKey_TextChanged(object sender, EventArgs e)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

命名規則で怒られてたので、大文字始まりにしておきました

textAPIKey.Text = setting.ApiKey;
checkDebugMode.Checked = setting.DebugMode;
checkIsOndemandEndpointEnabled.Checked = setting.IsOndemandEndpointEnabled;
var maxDegreeOfParallelism = setting.MaxDegreeOfParallelism;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

並行実行を渡す開発者オプションを削除します

public static PeriodSupportedTierResolver Create(IBuffettCodeApiClient apiClient, IApiResponseParser parser) => new PeriodSupportedTierResolver(apiClient, parser, new SupportedTierDictionary());

public SupportedTier Resolve(DataTypeConfig dataType, string ticker, IPeriod period)
public SupportedTier Resolve(DataTypeConfig dataType, string ticker, IPeriod period, bool isConfigureAwait, bool useCache)
Copy link
Copy Markdown
Contributor Author

@shoe116 shoe116 Nov 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isConfigureAwaituseCache を外から渡せるように変更しています

Assert.AreEqual(SupportedTier.OndemandTier, helper.FindAvailableTier(DataTypeConfig.Quarter, ticker, ondemandOldest.Next() as FiscalQuarterPeriod, true));
Assert.AreEqual(SupportedTier.OndemandTier, helper.FindAvailableTier(DataTypeConfig.Quarter, ticker, ondemandLatest, true));
Assert.ThrowsException<NotSupportedTierException>(() => helper.FindAvailableTier(DataTypeConfig.Quarter, ticker, ondemandLatest.Next() as FiscalQuarterPeriod, true));
Assert.AreEqual(SupportedTier.FixedTier, helper.FindAvailableTier(DataTypeConfig.Quarter, ticker, fixedOldest, true, true, true));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test は 両方とも true の、 UDF と同じ仕様で動かします

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSVダウンロードように、 ConfigureAwait(false) のケースもテストしたほうがいいと思う

var quarters = PeriodRange<FiscalQuarterPeriod>.Slice(parameters.Range, 12)
.SelectMany
(r => processor.GetApiResources(DataTypeConfig.Quarter, parameters.Ticker, r.From, r.To, true, true)
(r => processor.GetApiResources(DataTypeConfig.Quarter, parameters.Ticker, r.From, r.To, false, true)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここを true で渡すと、リボンから呼ぶとすごく遅くなるので false にします
https://devblogs.microsoft.com/dotnet/configureawait-faq/#why-would-i-want-to-use-configureawaitfalse

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConfigureAwait(true) にしたいケースがほとんど存在しないし (テストが書きやすいくらい?)、基本的に ConfigureAwait(false) でハードコードしたほうが安全な気がしてる

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UDFのほうは考慮する必要があるか

@shoe116 shoe116 requested a review from akiomik November 1, 2021 11:31
/// <summary>
/// 最大同時実行数のデフォルト値
/// </summary>
private static readonly uint MaxDegreeOfParallelismDefault = 8;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最大同時実行数を設定から消します

Copy link
Copy Markdown
Contributor

@akiomik akiomik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

単体テストで実APIを呼び出すのは単体テストの責務から外れてるように感じるので、モックを使うように書き直して欲しいです 🙏

@shoe116
Copy link
Copy Markdown
Contributor Author

shoe116 commented Nov 3, 2021

@akiomik 異常系については mock にできました。 b1717a3
正常系はだいぶ書き直しが必要なので、このPRを merge、リリース後下記で取り組みます
#45

Copy link
Copy Markdown
Contributor Author

@shoe116 shoe116 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akiomik ApiClientCore を mock できるようにしました

/// </summary>

public class ApiClientCore : IDisposable
public class ApiClientCore : IDisposable, IApiClientCore
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

実際にhttp通信を行うこいつを差し替えられるようにします

return new ApiClientCoreWithCache(apiClientCore, cacheHelper);
}

public static ApiClientCoreWithCache Create(string apiKey, Uri baseUrl, MemoryCache cache)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この Create は誰も使ってなかったので削除

}

public static ApiClientCoreWithCache Create(string apiKey, Uri baseUrl, MemoryCache cache)
public static ApiClientCoreWithCache Create(IApiClientCore apiClientCore, ApiRequestCacheHelper cacheHelper)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代わりにこっちを作りました

var mockApiClientCore = new ErrorMockApiClientCore("dummy");
var client = ApiClientCoreWithCache.Create(mockApiClientCore, CreateCheHelperForTest());
ApiGetRequest request = new ApiGetRequest("dummy endpoint", new Dictionary<string, string>());
Assert.ThrowsException<BuffettCodeApiClientException>(() => client.Get(request, false, false));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clientCore が例外を投げた場合はそのまま投げることを確認しています

Comment on lines +86 to +89
Assert.AreEqual(request.ToString(), client.Get(request, false, true));

// don't use cache
Assert.AreEqual(request.ToString(), client.Get(request, false, false));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cahceを使っても使わなくても、同じ結果が返ることを確認しています

var mockApiClientCore = new MockApiclientCore("dummy");
var client = ApiClientCoreWithCache.Create(mockApiClientCore, CreateCheHelperForTest());
ApiGetRequest request = new ApiGetRequest("dummy endpoint", new Dictionary<string, string>());
Assert.AreEqual(request.ToString(), client.Get(request, false, true));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1回目のリクエスト

[TestClass()]
public class ApiClientCoreWithCacheTests
{
private static ApiRequestCacheHelper CreateCheHelperForTest()
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test ごとにcache helper を作ります。

Comment on lines -62 to +61
return await response.Content.ReadAsStringAsync().ConfigureAwait(isConfigureAwait);
var content = response.Content.ReadAsStringAsync().Result;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReadAsStringAsync().Result って危険な呼び出しにみえるけど、非同期処理が未完了だったらブロックしてくれると。

https://docs.microsoft.com/ja-jp/dotnet/api/system.threading.tasks.task-1.result?view=net-5.0#--

Comment thread BuffettCodeAPIClient/ApiClientCore.cs Outdated
{
public interface IApiClientCore
{
Task<string> Get(ApiGetRequest request, bool isConfigureAwait);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using (var response = await httpClient.GetAsync(path).ConfigureAwait(isConfigureAwait)) しているのでシグニチャは Task<string> のまま、と

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これどうするかすごい迷ったけど、この ApiClientCore の通信部分は非同期に呼び出す前提にしておいたほうがいいと思う。

string 直接返す client が Task<string> に変換して返すのは全然難しくないし

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

将来的には非同期クライアントになってるほうがいいと思うので、内部的には半同期みたいな感じでもシグニチャはこのままでいいと思う

Comment on lines -31 to -32
Assert.ThrowsExceptionAsync<InvalidAPIKeyException>
(() => client.GetDaily("6502", day, true, true, false));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なるほど、こういうのがそのまま投げてるテストケースか

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そそ、 http 通信のlogic が処理の真ん中に入っちゃってるんだよね。そとから clientCore を渡す形の VxApiClientHelper みたいなやつを作って、各 client はその static な関数を呼ぶだけにしようと思う

Comment on lines +78 to +81
private void CheckDebugMode_CheckedChanged(object sender, EventArgs e)
{

}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これは勝手に定義されてるやつ?

Copy link
Copy Markdown
Contributor Author

@shoe116 shoe116 Nov 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

そう、formのUIをいじると勝手に作られる event handler 用の関数

@shoe116 shoe116 merged commit 56504a4 into master Nov 3, 2021
@shoe116 shoe116 deleted the faster_csv_download branch November 3, 2021 07:59
@shoe116 shoe116 mentioned this pull request Nov 3, 2021
1 task
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSV出力が非常に低速なので高速化する

2 participants