S2S APIレスポンスコードとエラー処理
SingularのS2S APIレスポンスコードを理解し、適切なエラー処理を実装することで、信頼性の高いデータ送信とアトリビューション精度を備えた堅牢な統合を実現します。
レスポンスアーキテクチャ
HTTPステータスコードの動作
重要な理解SingularのAPIと統合する場合、全てのレスポンスはHTTP 200ステータスコードを返し、成功('ok')または失敗('error')を判断するために、レスポンスボディの'status'フィールドを検証する必要があります。
レスポンスのペイロードには'reason' フィールドがあり、ステータスが'error' の場合は詳細なエラー情報を提供します。
レスポンスの構造:すべての API レスポンスは、成否にかかわらず一貫した JSON フォーマットに従います。これにより、すべてのエンドポイント間での標準化された解析とエラー処理が可能になります。
エラー処理戦略
実装のベストプラクティス
データの完全性とシステムの信頼性を確保するために、包括的なエラー処理戦略を実装する。
推奨されるアプローチ
- 再試行メカニズム:設定可能な最大試行回数を持つ指数バックオフ再試行を実装する。
- 順次処理:データの一貫性を確保するため、再試行中もリクエストの順序を維持する。
- エラーの分類:リトライ可能なエラーとリトライ不可能なエラーを区別する(無効なパラメータはリトライすべきではない
- 包括的なロギング:元のパラメータ、エラーメッセージ、デバイス識別子、タイムスタンプを含む、すべての失敗したリクエストをログに記録します。
- モニタリングとアラート:再試行を追跡し、重大な失敗に対する通知システムを実装する。
エクスポネンシャル・バックオフの実装
エクスポネンシャル・バックオフにより、一時的な障害時にサーバーに負荷がかかるのを防ぐと同時に、効率的な再試行戦略を提供します。
リトライ戦略
- 初期遅延:最初の失敗の後、1~2秒の遅延で開始
- バックオフ乗数:後続の再試行ごとに遅延時間を2倍にする(2秒→4秒→8秒→16秒)
- 最大再試行回数:上限を設定(推奨:3~5回
- 最大遅延:妥当な閾値で遅延の上限を設定(例:60秒
- ジッター:カミナリの群れ効果を防ぐためにランダムなジッターを追加する。
import requests
import time
import random
NON_RETRYABLE_KEYWORDS = ['invalid', 'missing', 'should have', 'no device id']
def is_retryable_error(reason):
"""A validation/parameter error is not retryable; anything else is."""
reason = (reason or '').lower()
return not any(keyword in reason for keyword in NON_RETRYABLE_KEYWORDS)
def exponential_backoff_request(url, params, max_retries=3):
"""Send an S2S API request with exponential-backoff retry logic."""
base_delay = 1 # seconds
max_delay = 60 # cap
for attempt in range(max_retries):
try:
response = requests.post(
url,
data=params,
headers={'Content-Type': 'application/x-www-form-urlencoded'},
timeout=10,
)
# Valid responses always return HTTP 200; check the body's status field.
if response.status_code == 200:
data = response.json()
if data.get('status') == 'ok':
return {'success': True, 'data': data}
reason = data.get('reason', '')
if not is_retryable_error(reason):
return {'success': False, 'reason': reason, 'retryable': False}
# Retryable error: fall through to the backoff delay.
except requests.exceptions.RequestException as error:
# Network/timeout errors are retryable.
if attempt == max_retries - 1:
return {'success': False, 'reason': str(error), 'retryable': True}
# Wait before the next attempt, with jitter to avoid a thundering herd.
if attempt < max_retries - 1:
delay = min(base_delay * (2 ** attempt), max_delay)
time.sleep(delay + random.uniform(0, 1))
return {'success': False, 'reason': 'max retries exceeded', 'retryable': True}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.*;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class SingularApiClient {
private static final int MAX_RETRIES = 3;
private static final long BASE_DELAY_MS = 1000;
private static final long MAX_DELAY_MS = 60000;
private static final HttpClient client = HttpClient.newHttpClient();
public static ApiResponse exponentialBackoffRequest(String url, Map<String, String> params) {
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
try {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/x-www-form-urlencoded")
.timeout(Duration.ofSeconds(10))
.POST(HttpRequest.BodyPublishers.ofString(buildFormBody(params)))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
// Valid responses always return HTTP 200; check the body's status field.
if (response.statusCode() == 200) {
JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject();
if ("ok".equals(json.get("status").getAsString())) {
return new ApiResponse(true, response.body(), false);
}
String reason = json.has("reason")
? json.get("reason").getAsString() : "Unknown error";
if (!isRetryableError(reason)) {
return new ApiResponse(false, reason, false);
}
// Retryable error: fall through to the backoff delay.
}
} catch (Exception error) {
// Network/timeout errors are retryable.
if (attempt == MAX_RETRIES - 1) {
return new ApiResponse(false, error.getMessage(), true);
}
}
// Wait before the next attempt, with jitter to avoid a thundering herd.
if (attempt < MAX_RETRIES - 1) {
long delay = Math.min(BASE_DELAY_MS * (1L << attempt), MAX_DELAY_MS);
long jitter = (long) (Math.random() * 1000);
try {
Thread.sleep(delay + jitter);
} catch (InterruptedException interrupted) {
Thread.currentThread().interrupt();
}
}
}
return new ApiResponse(false, "Max retries exceeded", true);
}
private static boolean isRetryableError(String reason) {
String lower = reason.toLowerCase();
for (String keyword : new String[] {"invalid", "missing", "should have", "no device id"}) {
if (lower.contains(keyword)) {
return false;
}
}
return true;
}
private static String buildFormBody(Map<String, String> params) {
StringBuilder body = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
if (body.length() > 0) {
body.append('&');
}
body.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8))
.append('=')
.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
}
return body.toString();
}
}
class ApiResponse {
public final boolean success;
public final String message;
public final boolean retryable;
public ApiResponse(boolean success, String message, boolean retryable) {
this.success = success;
this.message = message;
this.retryable = retryable;
}
}
ロギングとモニタリング
包括的なログは、迅速なトラブルシューティングを可能にし、統合の健全性を可視化します。
重要な情報をログに記録します:
- リクエストの詳細:すべてのパラメータを含む完全なリクエスト URL (機密データをマスク)
- レスポンスデータ:HTTPステータスコード、レスポンスボディ、ヘッダー
- エラーコンテキスト:エラーメッセージ、理由フィールド、エラー分類
- デバイス情報:特定のユーザーをトラブルシューティングするためのデバイス識別子
- タイムスタンプ:リクエスト・タイム、レスポンス・タイム、リトライ・タイムスタンプ
- 再試行のメタデータ:試行回数、バックオフ遅延、最終結果
モニタリング・メトリクス:タイプ別のエラー率、再試行成功率、平均応答時間、失敗したリクエスト量を追跡し、統合の問題を事前に特定します。
成功レスポンス
APIレスポンスの成功は、リクエストがSingularのシステムで処理されたことを示します。
HTTP 200 - 成功
| HTTPレスポンス | 説明 |
|---|---|
200 - ok
|
レスポンスボディにエラーや理由のない200 - okレスポンスは、リクエストが処理キューに正常に送信されたことを示します。 成功の指標:Status フィールドは "ok" を含み、"reason" フィールドはレスポンスに存在しない。 応答構造:
処理 注:「ok」ステータスはリクエストが処理のためにキューに入れられたことを示しますが、レポートでの即時の可視性を保証するものではありません。 |
エラーレスポンス
エラーレスポンスはリクエストの失敗に関する詳細情報を提供し、迅速なトラブルシューティングと解決を可能にします。
パラメータエラー
必須パラメータの欠落
| HTTP レスポンス | 説明 |
|---|---|
200 - error
|
理由を含むレスポンス: 原因原因: リクエストに必要なパラメータがないか、パラメータ値が空/nullです。 再試行不可能なエラー:このエラーは無効なリクエスト構造を示します。パラメータの問題を修正せずに再試行しないでください。 解決方法
レスポンスの例
よくあるパラメータの欠落: |
プラットフォームのエラー
無効なプラットフォーム値
| HTTP レスポンス | 説明 |
|---|---|
200 - error
|
理由を含むレスポンス: 原因原因: Platform パラメータ値がサポートされているプラットフォームのリストにないか、形式が正しくありません (大文字と小文字が区別されます)。 再送不可エラー:無効なパラメータ値のため、再提出の前に修正が必要です。 サポートされているプラットフォーム
解決方法
応答例
よくある間違いPC」の代わりに「desktop」を使う、プラットフォーム名を小文字にする、またはサポートされていないプラットフォーム値を使う |
デバイス識別子のエラー
デバイス識別子の欠落
| HTTPレスポンス | 説明 |
|---|---|
200 - error
|
理由を含むレスポンス: 原因原因: 指定されたプラットフォームに必要なデバイス識別子がありません。 再試行不可能なエラー:デバイス識別子は帰属に必要です。有効な識別子がない場合、リクエストは処理できません。 解決方法
レスポンスの例:
プラットフォーム固有の識別子に関する完全な文書については、『Device Identifier Requirements(デバイス識別子の要件)』を参照のこと。 |
200 - error
|
理由付きレスポンス: 原因原因:プラットフォームが指定されているが、そのプラットフォームに必要なデバイス識別子がないか、提供された識別子のタイプが正しくない。 再試行不可能なエラー:非再帰可能エラー:プラットフォーム識別子の不一致が帰属を妨げています。正しい識別子が必要です。 よくあるシナリオ
解決方法
レスポンスの例
|
ネットワークとインフラストラクチャーのエラー
200以外のHTTPステータス・コード
| HTTP レスポンス | 説明 |
|---|---|
4xx / 5xx
|
200以外のHTTPステータスコードは、インフラストラクチャまたはネットワークの問題を示します。 リトライ可能なエラー:これらのエラーは通常、指数関数的バックオフ再試行メカニズムを実装した過渡的なものです。 一般的なステータスコード
解決方法
レート制限:429エラーを受信した場合、リクエストレートを下げ、制限内に収まるようにリクエストスロットルを実装する。 |
エラーの分類
エラーの種類を理解することで、適切な再試行ロジックと迅速な問題解決が可能になります。
再試行可能エラーと再試行不可能エラー
再試行不可エラー
これらのエラーは、再送信前に手動での修正が必要な無効なリクエストパラメータまたは設定を示します。
| エラーパターン | アクション |
|---|---|
missing argument: {param}
|
リクエストに不足しているパラメータを追加する |
invalid platform: {platform}
|
プラットフォームの値をサポートされているオプションに修正する |
no device ID supplied
|
必要なデバイス識別子を含める |
platform: {platform} should have an {identifier} param
|
指定されたプラットフォームの正しい識別子を追加する |
再試行可能なエラー
これらのエラーは、指数関数的バックオフを使用した再試行によって解決する可能性のある一時的な問題を示しています。
| エラーの種類 | リトライ方法 |
|---|---|
| 200以外のHTTPステータスコード | 指数関数的バックオフによる再試行(2~3回) |
| ネットワーク・タイムアウト | 指数関数的バックオフによる再試行(3~5回) |
| 接続エラー | 指数関数的バックオフによる再試行(3~5回) |
| レート制限エラー(429) | バックオフ遅延を長くし、リクエスト・レートを下げる |
決定ロジック:理由フィールドの内容に基づいてエラーを分類する。 invalid」、「missing」、または「should have」を含むエラーは通常、再試行不可能である。 インフラストラクチャエラー(タイムアウト、接続障害、5xxコード)は再試行可能である。
トラブルシューティングガイド
API統合に関する一般的な問題を解決するためのクイックリファレンスです。
一般的な問題と解決策
パラメータの問題
| 症状 | 解決方法 |
|---|---|
| 引数がない |
|
| 無効なプラットフォームエラー |
大文字と小文字を区別する正しいプラットフォーム値を使用してください
|
| デバイスIDが提供されていない |
プラットフォーム固有のデバイスIDを含める
|
| プラットフォームが識別子を持つこと |
識別子の種類をプラットフォームに合わせる
|
リクエスト検証チェックリスト
飛行前の検証:一般的なエラーを防ぐため、リクエストを送信する前に以下の項目を確認してください。
-
SDK Key (
a) が含まれており、正しい (Reporting API Key ではありません) -
プラットフォーム (
p) の値がサポートされるリストに正確に一致する。 -
アプリ識別子(
i)がプラットフォームタイプ(iOSの場合はバンドルID、Androidの場合はパッケージ名)と一致する。 - デバイス識別子がプラットフォームに適切であり、NULL/空でないこと。
-
IP アドレス (
ip) またはuse_ip=trueを含む。 -
モバイルプラットフォーム用のOSバージョン(
ve)を含む - エンドポイントに必要なパラメータをすべて含む
-
必要に応じてURLエンコードされたパラメータ値(特に
eパラメータのJSON
サポートリソース
追加ヘルプ
エラー処理とトラブルシューティングを実施しても問題が解決しない場合は、以下のヘルプを参照してください:
- ドキュメント: S2S APIドキュメンテーションをご参照ください。
- テスト SDKコンソールを利用して、統合を検証する
- サポートエラーログ、リクエスト例、デバイス識別子をSingularサポートに連絡してください。
- ステータスの確認サービスインシデントのSingularシステムステータスの確認