BigQuery API クライアントライブラリを用いたJobのタイムアウト検知について

# この記事の目的
.NETでのデータベース接続でCommandTimeoutを設定する場合、SqlCommandやIDbCommand（ODBC)であればint型のCommandTimeoutプロパティに秒数を設定すればよいが、BigQueryClientの場合にはどのように値を設定すればよいかを記載する。

# 結論
結論としては、クエリ結果の取得をする際のオプションオブジェクトにjob完了までの待機時間を設定するのが最も
近しい指定方法となる。
サンプルコードは以下の通り。

```c#:GetQueryResult
public BigQueryResult GetResult(GoogleCredential credential, string query)
{
    BigQueryClient client = null;
    BigQueryJob job = null;
    try
    {
        BigQueryClient client = BigQueryClient.Create(ProjectId, credential);
        DatasetReference dsRef = new DatasetReference();
        // デフォルトデータセットを設定
        dsRef.DatasetId = Dataset;
        QueryOptions queryOptions = new QueryOptions();
        queryOptions.DefaultDataset = dsRef;
        // ジョブ作成
        job = client.CreateQueryJob(query, null, queryOptions);
        GetQueryResultsOptions queryResultOptions = new GetQueryResultsOptions();
        // ジョブのタイムアウトを秒数で設定
        queryResultOptions.Timeout = TimeSpan.FromSeconds(CommandTimeout);
        BigQueryResults results = client.GetQueryResults(job.Resource.JobReference, queryResultOptions).ThrowOnAnyError();
        return results;
    }
    catch (Exception xe)
    {
        if (xe is TimeoutException)
        {
            // タイムアウト時の処理
        }
        else
        {
            // タイムアウト時以外の処理
        }
        log.Error(e);
        throw;
    }
}


// タイムアウトまでの時間を設定。
int CommandTimeout { get {return 30; } }

// デフォルトのデータセット名
string Dataset { get { return "sampledataset"; } }
```

# 補足
タイムアウトというキーワードでBigQuery関連の情報を検索すると、jobがエラーで終了した場合にエラーコード"timeout"を含むケースがあると書かれている。この場合のtimeoutエラーは「DMLステートメントが 6 時間実行されていない場合」というかなり特殊な場合に発生するエラーであるため、CommandTimeoutに相当する時間を設定する場合には考慮しないくてもよいケースがほとんどである。
(参考：https://cloud.google.com/bigquery/quotas?hl=ja#data-manipulation-language-statements）
もし、ハンドリングしたいのであれば、発生時にはThorowOnAnyErrorでcatchに入ってくるので、そこで以下の様に処理をしてあげればよい。

```c#:timeout ハンドリング
public BigQueryResult GetResult(GoogleCredential credential, string query)
{
    BigQueryClient client = null;
    BigQueryJob job = null;
    try
    {
        // このあたりは、前のコードと同じ
        BigQueryClient client = BigQueryClient.Create(ProjectId, credential);
        DatasetReference dsRef = new DatasetReference();
        // デフォルトデータセットを設定
        dsRef.DatasetId = Dataset;
        QueryOptions queryOptions = new QueryOptions();
        queryOptions.DefaultDataset = dsRef;
        // ジョブ作成
        job = client.CreateQueryJob(query, null, queryOptions);
        GetQueryResultsOptions queryResultOptions = new GetQueryResultsOptions();
        // ジョブのタイムアウトを秒数で設定
        queryResultOptions.Timeout = TimeSpan.FromSeconds(CommandTimeout);
        BigQueryResults results = client.GetQueryResults(job.Resource.JobReference, queryResultOptions).ThrowOnAnyError();
        return results;
    }
    catch (Exception xe)
    {
        if (xe is TimeoutException)
        {
            // タイムアウト時の処理
        }
        else if(job.Status != null &&  job.Status.Errors != null)
        {
            if(job.Status.Errors.Any(x => x.Reason.ToLower() == "timeout"))
            {
                // timeout発生時のエラー処理を記述
            }
        }
        else
        {
            // タイムアウト時以外の処理
        }
        log.Error(e);
        throw;
    }
}
```