Go SDKのエラーを処理し、中断されたタスクを復旧する
タイムアウトは、タスクの作成が失敗したことを意味しません。元のタスク入力と SDK のリクエスト ID を保持してください。タスク ID があれば GetTask で再開し、なければ元の入力を一切変更せず、同じリクエスト ID で CreateTask を再実行します。
キャンセルで停止するのは Go クライアントの待機であり、リモートのタスクではありません。新たな解決に料金を支払う前に、そのタスクを復旧してください。
このページの内容
エラー処理の例を実行する
Go 1.22 以降と、公式の solvethree.com/sdk モジュールのバージョン v0.1.0 を使用します。設定については Go 連携手順を参照してください。
サーバー側の環境変数 SOLVETHREE_API_KEY と SOLVETHREE_PROXY を設定します。例の Site と Action を、対象のホスト名とアクションに置き換えます。そのサイトにはワークスペース内の有効なプロファイルが必要です。ホスト名を送信してもプロファイルは作成されません。この reCAPTCHA リクエストは単一のプロキシを使用し、Type や URL は不要です。
package main
import (
"context"
"fmt"
"errors"
"log"
"os"
"time"
solvethree "solvethree.com/sdk"
)
func main() {
client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
if err != nil { log.Fatal(err) }
ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
defer cancel()
input := solvethree.TaskRequest{Site: "accounts.example.com", Action: "login", Proxy: os.Getenv("SOLVETHREE_PROXY")}
result, err := client.Solve(ctx, input, solvethree.SolveOptions{})
if err != nil {
var failed *solvethree.TaskFailedError
var api *solvethree.APIError
var recovery *solvethree.Error
switch {
case errors.As(err, &failed):
log.Printf("Failed task %s: %s", failed.TaskID, failed.ErrorCode)
case errors.As(err, &recovery):
log.Printf("Recovery request=%s task=%s", recovery.RequestID, recovery.TaskID)
if errors.As(err, &api) { log.Printf("HTTP %d; retry delay %s", api.StatusCode, api.RetryAfter) }
// Save input plus recovery.RequestID. Resume GetTask for a known TaskID;
// otherwise replay CreateTask with identical inputs and that RequestID.
}
return
}
fmt.Println("Ready task:", result.ID)
// Submit result.Solution.Token with its browser identity and original proxy.
}この例ではコンテキストの期限を180秒に設定し、SDK エラーから復旧用 ID を取得できた場合にログへ記録します。ログ記録だけでは永続的な復旧はできません。エラーを破棄する前に、元の input、返された RequestID、取得できた TaskID を保存してください。
「準備完了のタスク」は、ソリューションが利用可能であることを意味し、送信先が受け入れたことを意味するわけではありません。SDK が代わりに送信することはありません。返されたブラウザー識別情報と、この単一プロキシの例では元のプロキシを使って、Solution.Token を送信してください。
タスクの失敗とリクエストの中断を区別する
ラップされたエラーは errors.As、キャンセルは errors.Is で調べます。これらのチェックはそれぞれ異なる点を確認します。1つのエラーが復旧用 ID を含み、HTTP エラーをラップしていて、なおかつ中断された処理として扱う必要がある場合もあります。
| 確認方法 | 意味と次の対応 |
|---|---|
errors.As で *solvethree.TaskFailedError を確認 | タスクが最終的に失敗しました。TaskID、RequestID、ErrorCode を確認します。GetTask で失敗したタスクを取得して Error のテキストを確認し、そのタスクのポーリングを停止してください。入力を変更せずに無条件に再送しないでください。 |
errors.Is で context.DeadlineExceeded または context.Canceled を確認 | 呼び出し元が待機を停止しました。これだけでは、作成が成功したか、リモートのタスクが完了したかは判断できません。復旧情報を保持し、必要に応じて新しいコンテキストで再開してください。 |
errors.As で *solvethree.Error を確認 | SDK のラッパーからは RequestID と TaskID を取得でき、根本の Cause がラップされています。ID は、元の入力を正確に保持したものと一緒に保存してください。 |
errors.As で *solvethree.APIError を確認 | HTTP の StatusCode と RetryAfter の期間を確認します。HTTP エラーは、ステータスが failed のタスクとは異なります。成功したタスクのペイロードを受け取ったと判断しないでください。 |
この例では、復旧処理の分岐内でネストされた APIError を確認します。アプリケーションで期限切れと明示的なキャンセルを区別する必要がある場合は、errors.Is でキャンセルを別途確認してください。
Solve はタスクを一度だけ作成します。再試行するのは HTTP 429、502、503、504 の一時的なポーリングレスポンスのみで、コンテキストの期限内で Retry-After に従います。CreateTask が POST を自動で再試行することはありません。
既存のタスクを再開する
- 1.
元のリクエストをそのまま保持する
プロキシまたは候補リストを含むすべてのタスク入力を、リクエスト ID と一緒に保存してください。CreateTask は、リクエスト ID を省略すると自動生成します。復旧用エラーから取得できる生成済みの値を記録してください。後から、変更されている可能性のある設定を使ってリクエストを再構築しないでください。
- 2.
タスク ID がわかっている場合は GetTask を使う
以前のコンテキストが期限切れまたはキャンセル済みの場合は、新しいコンテキストを作成し、保存した TaskID を使って GetTask で取得します。ステータスが pending または solving なら待機を続け、ready の場合にのみソリューションを使用し、failed なら停止します。
- 3.
タスク ID が不明な場合にのみ作成を再実行する
保存しておいた元の TaskRequest の RequestID に、復旧したリクエスト ID を設定し、同じ入力で CreateTask を呼び出します。同じ ID とリクエストを再利用すると、既存のタスクが返されます。同じ ID を再利用しながら入力を変更すると競合が発生します。
接続がタイムアウトしたという理由だけで、新しいリクエスト ID を使用しないでください。それでは状態が不明なタスクを復旧するのではなく、新しい論理タスクを開始することになります。まず元のタスクに何が起きたかを確認してください。失敗していた場合は、そのエラーを調べてから、修正したリクエストが必要かどうかを判断します。
参照元とAPIリファレンス
最初のタスクを実行しましょう。
$5のワークスペースクレジットで始められます。