Testing a UDA
Introduction
User-Defined Analytics (UDAs) are custom functions in kdb+ designed for complex data aggregation across multiple sources. Thorough testing and debugging of UDAs is essential to ensure they function correctly. This page provides a step-by-step approach to testing and debugging query and aggregation functions within a UDA, helping you identify and resolve errors to ensure accurate and efficient data processing.
This page covers the following sections:
-
Testing query functions
-
Testing aggregation functions
Testing query functions
When creating a query, you may encounter bugs that require investigation and debugging. The easiest way to debug an issue is to attach to a DAP and run the code directly, with error trapping enabled. For more information on error trapping, refer to the error trap clients section.
To test query functions you can also use the Q query option in the Query Window. For more information on using the Query Window, refer to the Using a Q based query section.
Step 1 - Attach to the DAP process
-
Attach to a DAP to run the code directly. This approach enables real-time debugging with error trapping.
-
Enable error trapping by running the following commands:
q).kxi.pe.disable[] / Disable protected evaluation
q)\e 1 / Enable kdb+ error trapping
Step 2 - Run the query and analyze the logs
-
If a query runs through the framework and encounters an error, but the framework catches and moves past it, review the DAP logs.
-
For example, consider the following query function, which generates a type error:
.custom.countBy:{[table;startTS;endTS;byCols]
1+`
}
2024-08-12 15:45:30.146 [hdb] DEBUG KXCTX {23329698-9642-4654-b65e-81bfefe16b05} Starting context, corr=23329698-9642-4654-b65e-81bfefe16b05
2024-08-12 15:45:30.146 [hdb] DEBUG KXCTX {23329698-9642-4654-b65e-81bfefe16b05} Setting context to no-save, reason='requests not saved in DAP'
2024-08-12 15:45:30.146 [hdb] DEBUG DA {23329698-9642-4654-b65e-81bfefe16b05} Executing .custom.countBy
2024-08-12 15:45:30.146 [hdb] ERROR SAPI {23329698-9642-4654-b65e-81bfefe16b05} Error (type) encountered executing .custom.countBy, rc=6 ac=11 ai=Unexpected error (type) encountered executing .custom.countBy
2024-08-12 15:45:30.146 [hdb] DEBUG DA {23329698-9642-4654-b65e-81bfefe16b05} Completed .custom.countBy, rc=6 ac=11 ai=Unexpected error (type) encountered executing .custom.countBy
2024-08-12 15:45:30.146 [hdb] DEBUG DA {23329698-9642-4654-b65e-81bfefe16b05} Sending response to aggregator, agg=:10.244.0.9:5070
2024-08-12 15:45:30.147 [hdb] DEBUG KXCTX {23329698-9642-4654-b65e-81bfefe16b05} Ending context, no-save
- The log indicates a
type
error, which helps identify that the issue is related to data types.
Step 3 - Enable detailed debugging
-
To pinpoint the exact location of the failure, re-run the query while connected to the process with error trapping enabled This approach generates more detailed debugging output.
-
Review the detailed output:
q)2024-08-12 15:51:51.154 [hdb] DEBUG KXCTX {ba62e0b9-f290-4abd-be3d-dcc91cb6116d} Starting context, corr=ba62e0b9-f290-4abd-be3d-dcc91cb6116d
2024-08-12 15:51:51.154 [hdb] DEBUG KXCTX {ba62e0b9-f290-4abd-be3d-dcc91cb6116d} Setting context to no-save, reason='requests not saved in DAP'
2024-08-12 15:51:51.154 [hdb] DEBUG DA {ba62e0b9-f290-4abd-be3d-dcc91cb6116d} Executing .custom.countBy
type error
.custom.countBy[0] {[table;startTS;endTS;byCols]1+`}
^
q))table / See what current table argument is
`trade
q))startTS / See what current startTS argument is
-0Wp
- Based on the output you can see specifically where the function is failing. Additionally, you can inspect the current values of the arguments. For larger functions, you can also run through the code line by line to identify the underlying issue.
Testing aggregation functions
When creating a UDA, it is essential to develop an aggregation function that consolidates partial results from multiple DAPs. If a bug exists in the aggregation logic, identifying the root cause can be challenging when an aggregation fails.
To assist in debugging, there are options available to have the aggregator return the partial results of a failed aggregation back to the caller. This allows for debugging within the same session. When the partial results are returned, the response includes an application code (ac
) in the header, describing the type of aggregation failure, and an rc
value of 100h
(PARTIALS).
If the partialsSent
field is present and set to true, the payload will consist solely of the partial results from each DAP involved in the request. By default, partial results are not sent when an aggregation fails. However, you can configure the aggregator to send back partial results using two methods:
-
Set the
sendPartials
value in the request header -
Configure the aggregator to always send partial results
Step 1 - Enable partial results on aggregation failure
To debug failed aggregations, configure the aggregator to return partial results:
-
Option 1 - Set the
sendPartials
value in the request header- Include the
sendPartials
field in the request header sent to the gateway. This approach ensures that the aggregator returns partial results if an aggregation failure occurs.
2. Option 2 - Configure the aggregator to always send partial resultsq)GATEWAY:10i / Handle to gateway q)args:enlist[`table]!enlist `trade q)GATEWAY (`.kxi.getData; args;`ignored;enlist[`sendPartials]!enlist 1b)
- Set the environment variable
KXI_SEND_PARTIALS
totrue
on the aggregator. This configuration ensures that any failed aggregation request automatically sends partial results back in the payload.
- Include the
Step 2 - Analyze the aggregator's response
If sendPartials
is enabled (either through the KXI_SEND_PARTIALS
environment variable or through the opts of a request) and the request errors in the aggregator, the response contains partial results. The response format depends on the specific error encountered:
-
Return Code =
PARTIALS (100)
- The payload is a list of partial responses from each DAP that participated in the request:
q)`rc`ac`ai#response 0 rc| 100h ac| 30h ai| "Unexpected error (mismatch) encountered aggregating myAPI" q)response 1 +(,`x)!,1 2 +(,`y)!,3 4
-
Return Code =
PARTIALS_SUB (101)
- If one or more sub-requests fail, the payload is a table that includes the response
header
andpayload
of each failed sub-request.
- If one or more sub-requests fail, the payload is a table that includes the response
-
Return Code =
PARTIALS_RESUME (102)
- The payload is a dictionary that contains
partials
,context
, andcallback
information. <!-- TODO: Add an example to explain nested API calls>
- The payload is a dictionary that contains