メインコンテンツまでスキップ

Debug Point

Debug pointは、FEまたはBEコードに挿入されるコードの一部で、プログラムがこのコードに到達すると、

プログラムの変数や動作を変更することができます。

主に、通常の手段では一部の例外をトリガーすることが不可能な場合のunit testやregression testに使用されます。

各debug pointには名前があり、名前は任意に設定でき、debug pointを有効化および無効化するスイッチがあり、

debug pointにデータを渡すこともできます。

FEとBEの両方がdebug pointをサポートしており、debug pointコードを挿入した後は、FEまたはBEの再コンパイルが必要です。

Code Example

FE example

private Status foo() {
// dbug_fe_foo_do_nothing is the debug point name
// when it's active, DebugPointUtil.isEnable("dbug_fe_foo_do_nothing") returns true
if (DebugPointUtil.isEnable("dbug_fe_foo_do_nothing")) {
return Status.Nothing;
}

do_foo_action();

return Status.Ok;
}

BE の例

void Status foo() {
// dbug_be_foo_do_nothing is the debug point name
// when it's active, DBUG_EXECUTE_IF will execute the code block
DBUG_EXECUTE_IF("dbug_be_foo_do_nothing", { return Status.Nothing; });

do_foo_action();

return Status.Ok;
}

Global Config

debug pointをグローバルに有効にするには、enable_debug_pointsをtrueに設定する必要があります。

enable_debug_pointsはFEのfe.confとBEのbe.confに配置されています。

指定されたDebug Pointを有効化する

debug pointがグローバルに有効化された後、debug point名を含むhttpリクエストをFEまたはBEノードに送信する必要があります。
その後、プログラムが指定されたdebug pointに到達した時のみ、関連するコードが実行されます。

API

POST /api/debug_point/add/{debug_point_name}[?timeout=<int>&execute=<int>]

クエリパラメータ

  • debug_point_name デバッグポイント名。必須パラメータ。

  • timeout タイムアウト(秒)。タイムアウト時、デバッグポイントは非アクティブ化されます。デフォルトは -1、タイムアウトなし。オプション。

  • execute アクティブ化後、デバッグポイントが実行可能な最大回数。デフォルトは -1、無制限。オプション。

リクエストボディ

なし

レスポンス

{
msg: "OK",
code: 0
}

デバッグポイントfooをアクティベート後、最大5回まで実行されます。

curl -X POST "http://127.0.0.1:8030/api/debug_point/add/foo?execute=5"

カスタムパラメータの受け渡し

debug pointを有効化する際、上記で言及した「timeout」と「execute」に加えて、カスタムパラメータを受け渡すことも可能です。
パラメータは、debug point名の後に文字'?'で結合されたurlパス内の「key=value」形式のキーと値のペアです。
以下の例を参照してください。

API

POST /api/debug_point/add/{debug_point_name}[?k1=v1&k2=v2&k3=v3...]
  • k1=v1
    k1はパラメータ名
    v1はパラメータ値
    複数のキーと値のペアは&で連結される

リクエストボディ

なし

レスポンス

{
msg: "OK",
code: 0
}

fe.conf で http_port=8030 に設定された FE ノードがあると仮定して、
以下の http リクエストは FE ノード内の foo という名前のデバッグポイントをアクティブ化し、パラメータ percentduration を渡します:

注意:ユーザー名とパスワードが必要な場合があります。

curl -u root: -X POST "http://127.0.0.1:8030/api/debug_point/add/foo?percent=0.5&duration=3"
NOTE:
1. Inside FE and BE code, names and values of parameters are taken as strings.
2. Parameter names and values are case sensitive in http request and FE/BE code.
3. FE and BE share same url paths of REST API, it's just their IPs and Ports are different.

FEとBEコードでパラメータを使用する

以下のリクエストは、FEでデバッグポイントOlapTableSink.write_random_choose_sinkをアクティベートし、パラメータneedCatchUpsinkNumを渡します:

curl -u root: -X POST "http://127.0.0.1:8030/api/debug_point/add/OlapTableSink.write_random_choose_sink?needCatchUp=true&sinkNum=3"

FE のコードは debug point OlapTableSink.write_random_choose_sink をチェックし、パラメータ値を取得します:

private void debugWriteRandomChooseSink(Tablet tablet, long version, Multimap<Long, Long> bePathsMap) {
DebugPoint debugPoint = DebugPointUtil.getDebugPoint("OlapTableSink.write_random_choose_sink");
if (debugPoint == null) {
return;
}
boolean needCatchup = debugPoint.param("needCatchUp", false);
int sinkNum = debugPoint.param("sinkNum", 0);
...
}

以下のリクエストは、BEでデバッグポイントTxnManager.prepare_txn.random_failedをアクティブ化し、パラメータpercentを渡します:

curl -X POST "http://127.0.0.1:8040/api/debug_point/add/TxnManager.prepare_txn.random_failed?percent=0.7

BEのコードはデバッグポイントTxnManager.prepare_txn.random_failedをチェックし、パラメータ値を取得します:

DBUG_EXECUTE_IF("TxnManager.prepare_txn.random_failed",
{if (rand() % 100 < (100 * dp->param("percent", 0.5))) {
LOG_WARNING("TxnManager.prepare_txn.random_failed random failed");
return Status::InternalError("debug prepare txn random failed");
}}
);

Debug Point を無効化

API

	POST /api/debug_point/remove/{debug_point_name}

クエリパラメータ

  • debug_point_name デバッグポイント名。必須パラメータ。

リクエストボディ

なし

レスポンス

{
msg: "OK",
code: 0
}

デバッグポイント foo を無効にします。

curl -X POST "http://127.0.0.1:8030/api/debug_point/remove/foo"

デバッグポイントをクリア

API

POST /api/debug_point/clear

リクエストボディ

なし

レスポンス

{
msg: "OK",
code: 0
}

curl -X POST "http://127.0.0.1:8030/api/debug_point/clear"

Regression Test における Debug Points

コミュニティの CI システムでは、FE と BE の enable_debug_points 設定はデフォルトで true になっています。

Regression test フレームワークは、特定の debug point を有効化および無効化するメソッドも提供します。
これらは以下のように宣言されています:

// "name" is the debug point to activate, "params" is a list of key-value pairs passed to debug point
def enableDebugPointForAllFEs(String name, Map<String, String> params = null);
def enableDebugPointForAllBEs(String name, Map<String, String> params = null);
// "name" is the debug point to deactivate
def disableDebugPointForAllFEs(String name);
def disableDebugPointForAllFEs(String name);

エラーを生成したいテストアクションの前にenableDebugPointForAllFEs()またはenableDebugPointForAllBEs()を呼び出す必要があり、
その後にdisableDebugPointForAllFEs()またはdisableDebugPointForAllBEs()を呼び出す必要があります。

並行実行の問題

有効化されたデバッグポイントはFEまたはBEにグローバルに影響するため、プルリクエストで他の並行テストが予期せず失敗する可能性があります。
これを回避するため、デバッグポイントを使用するリグレッションテストはregression-test/suites/fault_injection_p0ディレクトリに配置する必要があり、
グループ名は"nonConcurrent"である必要があるという規約があります。これらのリグレッションテストはプルリクエストワークフローによって順次実行されるためです。

// .groovy file of the test case must be in regression-test/suites/fault_injection_p0
// and the group name must be 'nonConcurrent'
suite('debugpoint_action', 'nonConcurrent') {
try {
// Activate debug point named "PublishVersionDaemon.stop_publish" in all FE
// and pass parameter "timeout"
// "execute" and "timeout" are pre-existing parameters, usage is mentioned above
GetDebugPoint().enableDebugPointForAllFEs('PublishVersionDaemon.stop_publish', [timeout:1])

// Activate debug point named "Tablet.build_tablet_report_info.version_miss" in all BE
// and pass parameter "tablet_id", "version_miss" and "timeout"
GetDebugPoint().enableDebugPointForAllBEs('Tablet.build_tablet_report_info.version_miss',
[tablet_id:'12345', version_miss:true, timeout:1])

// Test actions which will run into debug point and generate error
sql """CREATE TABLE tbl_1 (k1 INT, k2 INT)
DUPLICATE KEY (k1)
DISTRIBUTED BY HASH(k1)
BUCKETS 3
PROPERTIES ("replication_allocation" = "tag.location.default: 1");
"""
sql "INSERT INTO tbl_1 VALUES (1, 10)"
sql "INSERT INTO tbl_1 VALUES (2, 20)"
order_qt_select_1_1 'SELECT * FROM tbl_1'

} finally {
// Deactivate debug points
GetDebugPoint().disableDebugPointForAllFEs('PublishVersionDaemon.stop_publish')
GetDebugPoint().disableDebugPointForAllBEs('Tablet.build_tablet_report_info.version_miss')
}
}