diff --git a/localenv/telemetry/grafana/provisioning/dashboards/example.json b/localenv/telemetry/grafana/provisioning/dashboards/example.json index 13c1a1838a..276dd8c647 100644 --- a/localenv/telemetry/grafana/provisioning/dashboards/example.json +++ b/localenv/telemetry/grafana/provisioning/dashboards/example.json @@ -23,10 +23,79 @@ "panels": [ { "datasource": { - "default": true, + "default": false, "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 0, + "y": 0 + }, + "id": 17, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "avg_over_time(rate(transactions_total[30s])[5m:])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Panel Title", + "type": "stat" + }, + { + "datasource": { + "default": false, + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "", "fieldConfig": { "defaults": { "color": { @@ -84,17 +153,15 @@ "overrides": [] }, "gridPos": { - "h": 6, - "w": 12, - "x": 0, + "h": 7, + "w": 21, + "x": 3, "y": 0 }, - "id": 12, + "id": 10, "options": { "legend": { - "calcs": [ - "p95" - ], + "calcs": ["p95"], "displayMode": "table", "placement": "right", "showLegend": true @@ -108,10 +175,10 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(connector_middleware_sum[$__rate_interval])/rate(connector_middleware_count[$__rate_interval]))", + "expr": "sum by (callName) (rate(getPendingPayment_sum[$__rate_interval])/rate(getPendingPayment_count[$__rate_interval]))", "instant": false, "legendFormat": "{{caller}}", "range": true, @@ -120,10 +187,10 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(connector_middleware_stack_sum[$__rate_interval])/rate(connector_middleware_stack_count[$__rate_interval]))", + "expr": "sum by (callName) (rate(handleSending_sum[$__rate_interval])/rate(handleSending_count[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", @@ -133,24 +200,175 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(balanceMiddleware_sum[$__rate_interval])/rate(balanceMiddleware_count[$__rate_interval]))", + "expr": "sum by (callName) (rate(getReceiver_sum[$__rate_interval])/rate(getReceiver_count[$__rate_interval]))", "hide": false, "instant": false, "legendFormat": "__auto", "range": true, "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (callName) (rate(psql_getAccountTotalSent_sum[$__rate_interval])/rate(psql_getAccountTotalSent_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (callName) (rate(tb_getAccountTotalSent_sum[$__rate_interval])/rate(tb_getAccountTotalSent_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (callName) (rate(handleCompleted_sum[$__rate_interval])/rate(handleCompleted_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "F" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (callName) (rate(sendWebhookEvent_sum[$__rate_interval])/rate(sendWebhookEvent_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "G" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (callName) (rate(handleFailed_sum[$__rate_interval])/rate(handleFailed_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "H" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (callName) (rate(processPendingPayment_sum[$__rate_interval])/rate(processPendingPayment_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "I" } ], - "title": "ILP Connector", + "title": "Outgoing Payment Worker", "type": "timeseries" }, { "datasource": { + "name": "${PrometheusDS}", + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 6, + "options": { + "displayMode": "gradient", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum(rate(traces_spanmetrics_latency_bucket{span_name=~\"^(mutation|query).*\"}[$__rate_interval])) by (le, span_name))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graphql Resolver Duration (95th Percentile)", + "type": "bargauge" + }, + { + "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -212,14 +430,12 @@ "h": 6, "w": 12, "x": 12, - "y": 0 + "y": 7 }, "id": 9, "options": { "legend": { - "calcs": [ - "p95" - ], + "calcs": ["p95"], "displayMode": "table", "placement": "right", "showLegend": true @@ -233,7 +449,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "rate(quote_service_create_get_quote_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_quote_time_ms_count[$__rate_interval])", @@ -246,7 +462,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "rate(ilp_rate_probe_time_ms_sum[$__rate_interval])/rate(ilp_rate_probe_time_ms_count[$__rate_interval])", @@ -259,7 +475,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "rate(ilp_get_quote_rate_time_ms_sum[$__rate_interval])/rate(ilp_get_quote_rate_time_ms_count[$__rate_interval])", @@ -272,7 +488,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "rate(ilp_make_ilp_plugin_connect_time_ms_sum[$__rate_interval])/rate(ilp_make_ilp_plugin_connect_time_ms_count[$__rate_interval])", @@ -285,7 +501,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "rate(ilp_plugin_close_connect_time_ms_sum[$__rate_interval])/rate(ilp_plugin_close_connect_time_ms_count[$__rate_interval])", @@ -298,7 +514,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "rate(ilp_plugin_disconnect_time_ms_sum[$__rate_interval])/rate(ilp_plugin_disconnect_time_ms_count[$__rate_interval])", @@ -314,8 +530,9 @@ }, { "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -374,17 +591,15 @@ "overrides": [] }, "gridPos": { - "h": 5, + "h": 6, "w": 12, - "x": 0, - "y": 6 + "x": 12, + "y": 13 }, - "id": 7, + "id": 11, "options": { "legend": { - "calcs": [ - "p95" - ], + "calcs": ["p95"], "displayMode": "table", "placement": "right", "showLegend": true @@ -398,23 +613,121 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "rate(quote_service_create_time_ms_sum[$__rate_interval])/rate(quote_service_create_time_ms_count[$__rate_interval])", + "expr": "sum by (callName) (rate(psql_getAccountBalances_sum[$__rate_interval])/rate(psql_getAccountBalances_count[$__rate_interval]))", "instant": false, - "legendFormat": "{{description}}", + "legendFormat": "__auto", "range": true, "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" + } + ], + "title": "PSQL Get Account Balance", + "type": "timeseries" + }, + { + "datasource": { + "name": "${PrometheusDS}", + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" }, - "editorMode": "code", - "expr": "rate(quote_service_create_resolve_receiver_time_ms_sum[$__rate_interval])/rate(quote_service_create_resolve_receiver_time_ms_count[$__rate_interval])", - "hide": false, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 19 + }, + "id": 14, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_create_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_create_time_ms_count[$__rate_interval])", + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_create_quote_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_create_quote_time_ms_count[$__rate_interval])", + "hide": false, "instant": false, "legendFormat": "{{description}}", "range": true, @@ -423,23 +736,23 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "rate(quote_service_create_get_quote_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_quote_time_ms_count[$__rate_interval])", + "expr": "rate(outgoing_payment_service_getwalletaddress_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_getwalletaddress_time_ms_count[$__rate_interval])", "hide": false, "instant": false, "legendFormat": "{{description}}", "range": true, - "refId": "C" + "refId": "E" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "rate(quote_service_create_get_latest_fee_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_latest_fee_time_ms_count[$__rate_interval])", + "expr": "rate(outgoing_payment_service_insertgrant_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_insertgrant_time_ms_count[$__rate_interval])", "hide": false, "instant": false, "legendFormat": "{{description}}", @@ -449,37 +762,77 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "rate(quote_service_create_insert_time_ms_sum[$__rate_interval])/rate(quote_service_create_insert_time_ms_count[$__rate_interval])", + "expr": "rate(outgoing_payment_service_insertpayment_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_insertpayment_time_ms_count[$__rate_interval])", "hide": false, "instant": false, "legendFormat": "{{description}}", "range": true, - "refId": "E" + "refId": "F" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "rate(quote_service_finalize_quote_ms_sum[$__rate_interval])/rate(quote_service_finalize_quote_ms_count[$__rate_interval])", + "expr": "rate(outgoing_payment_service_getreceiver_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_getreceiver_time_ms_count[$__rate_interval])", "hide": false, "instant": false, "legendFormat": "{{description}}", "range": true, - "refId": "F" + "refId": "H" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_getpeer_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_getpeer_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "G" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_patchpeer_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_patchpeer_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "I" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_webhook_event_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_webhook_event_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "C" } ], - "title": "Quote Service Create Time (ms)", + "title": "Outgoing Payment Service Create Time (ms)", "type": "timeseries" }, { "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -532,23 +885,20 @@ "value": 80 } ] - }, - "unit": "ms" + } }, "overrides": [] }, "gridPos": { - "h": 5, + "h": 6, "w": 12, "x": 12, - "y": 6 + "y": 19 }, - "id": 11, + "id": 13, "options": { "legend": { - "calcs": [ - "p95" - ], + "calcs": ["p95"], "displayMode": "table", "placement": "right", "showLegend": true @@ -562,24 +912,128 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(psql_getAccountBalances_sum[$__rate_interval])/rate(psql_getAccountBalances_count[$__rate_interval]))", + "expr": "sum by (callName) (rate(createLiquidityAccount_sum[$__rate_interval])/rate(createLiquidityAccount_count[$__rate_interval]))", "instant": false, "legendFormat": "__auto", "range": true, "refId": "A" } ], - "title": "PSQL Get Account Balance", + "title": "Ilp Connector > Balance Middleware > createLiquidityAccount", "type": "timeseries" }, { "datasource": { - "default": true, + "name": "${TempoDS}", + "type": "tempo", + "uid": "${TempoDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Duration" + } + ] + }, + "pluginVersion": "11.2.0", + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "${TempoDS}" + }, + "filters": [ + { + "id": "9bab4a0a", + "operator": "=", + "scope": "span" + }, + { + "id": "service-name", + "operator": "=", + "scope": "resource", + "tag": "service.name", + "value": ["RAFIKI_NETWORK"], + "valueType": "string" + }, + { + "id": "span-name", + "operator": "=", + "scope": "span", + "tag": "name", + "value": [], + "valueType": "string" + }, + { + "id": "min-duration", + "operator": ">", + "tag": "duration", + "value": "100ms", + "valueType": "duration" + } + ], + "limit": 20, + "queryType": "traceqlSearch", + "refId": "A", + "tableType": "traces" + } + ], + "title": "Traces > 100ms", + "type": "table" + }, + { + "datasource": { + "default": false, "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -632,8 +1086,7 @@ "value": 80 } ] - }, - "unit": "ms" + } }, "overrides": [] }, @@ -641,16 +1094,14 @@ "h": 6, "w": 12, "x": 0, - "y": 11 + "y": 26 }, - "id": 10, + "id": 16, "options": { "legend": { - "calcs": [ - "p95" - ], - "displayMode": "table", - "placement": "right", + "calcs": [], + "displayMode": "list", + "placement": "bottom", "showLegend": true }, "tooltip": { @@ -665,112 +1116,197 @@ "uid": "PBFA97CFB590B2093" }, "editorMode": "code", - "expr": "sum by (callName) (rate(getPendingPayment_sum[$__rate_interval])/rate(getPendingPayment_count[$__rate_interval]))", + "expr": "rate(depositOutgoingPaymentLiquidity_sum[$__rate_interval])/rate(depositOutgoingPaymentLiquidity_count[$__rate_interval])", "instant": false, - "legendFormat": "{{caller}}", + "legendFormat": "{{callName}}", "range": true, "refId": "A" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(handleSending_sum[$__rate_interval])/rate(handleSending_count[$__rate_interval]))", + "expr": "rate(fundPayment_sum[$__rate_interval])/rate(fundPayment_count[$__rate_interval])", "hide": false, "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{callName}}", "range": true, "refId": "B" + } + ], + "title": "Deposit Outgoing Payment Liquidity ", + "type": "timeseries" + }, + { + "datasource": { + "name": "${PrometheusDS}", + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 7, + "options": { + "legend": { + "calcs": ["p95"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(getReceiver_sum[$__rate_interval])/rate(getReceiver_count[$__rate_interval]))", - "hide": false, + "expr": "rate(quote_service_create_time_ms_sum[$__rate_interval])/rate(quote_service_create_time_ms_count[$__rate_interval])", "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{description}}", "range": true, - "refId": "C" + "refId": "A" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(psql_getAccountTotalSent_sum[$__rate_interval])/rate(psql_getAccountTotalSent_count[$__rate_interval]))", + "expr": "rate(quote_service_create_resolve_receiver_time_ms_sum[$__rate_interval])/rate(quote_service_create_resolve_receiver_time_ms_count[$__rate_interval])", "hide": false, "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{description}}", "range": true, - "refId": "D" + "refId": "B" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(tb_getAccountTotalSent_sum[$__rate_interval])/rate(tb_getAccountTotalSent_count[$__rate_interval]))", + "expr": "rate(quote_service_create_get_quote_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_quote_time_ms_count[$__rate_interval])", "hide": false, "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{description}}", "range": true, - "refId": "E" + "refId": "C" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(handleCompleted_sum[$__rate_interval])/rate(handleCompleted_count[$__rate_interval]))", + "expr": "rate(quote_service_create_get_latest_fee_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_latest_fee_time_ms_count[$__rate_interval])", "hide": false, "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{description}}", "range": true, - "refId": "F" + "refId": "D" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(sendWebhookEvent_sum[$__rate_interval])/rate(sendWebhookEvent_count[$__rate_interval]))", + "expr": "rate(quote_service_create_insert_time_ms_sum[$__rate_interval])/rate(quote_service_create_insert_time_ms_count[$__rate_interval])", "hide": false, "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{description}}", "range": true, - "refId": "G" + "refId": "E" }, { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(handleFailed_sum[$__rate_interval])/rate(handleFailed_count[$__rate_interval]))", + "expr": "rate(quote_service_finalize_quote_ms_sum[$__rate_interval])/rate(quote_service_finalize_quote_ms_count[$__rate_interval])", "hide": false, "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{description}}", "range": true, - "refId": "H" + "refId": "F" } ], - "title": "Outgoing Payment Worker", + "title": "Quote Service Create Time (ms)", "type": "timeseries" }, { "datasource": { - "default": true, + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -823,22 +1359,21 @@ "value": 80 } ] - } + }, + "unit": "ms" }, "overrides": [] }, "gridPos": { "h": 6, "w": 12, - "x": 12, - "y": 11 + "x": 0, + "y": 38 }, - "id": 13, + "id": 12, "options": { "legend": { - "calcs": [ - "p95" - ], + "calcs": ["p95"], "displayMode": "table", "placement": "right", "showLegend": true @@ -852,203 +1387,50 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "sum by (callName) (rate(createLiquidityAccount_sum[$__rate_interval])/rate(createLiquidityAccount_count[$__rate_interval]))", + "expr": "sum by (callName) (rate(connector_middleware_sum[$__rate_interval])/rate(connector_middleware_count[$__rate_interval]))", "instant": false, - "legendFormat": "__auto", + "legendFormat": "{{caller}}", "range": true, "refId": "A" - } - ], - "title": "Ilp Connector > Balance Middleware > createLiquidityAccount", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "PBFA97CFB590B2093" - }, - "description": "", - "fieldConfig": { - "defaults": { - "color": { - "mode": "continuous-GrYlRd" - }, - "fieldMinMax": false, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 12, - "w": 12, - "x": 0, - "y": 17 - }, - "id": 6, - "options": { - "displayMode": "gradient", - "maxVizHeight": 300, - "minVizHeight": 16, - "minVizWidth": 8, - "namePlacement": "auto", - "orientation": "horizontal", - "reduceOptions": { - "calcs": [ - "lastNotNull" - ], - "fields": "", - "values": false }, - "showUnfilled": true, - "sizing": "auto", - "valueMode": "color" - }, - "pluginVersion": "11.2.0", - "targets": [ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", - "expr": "histogram_quantile(0.95, sum(rate(traces_spanmetrics_latency_bucket{span_name=~\"^(mutation|query).*\"}[$__rate_interval])) by (le, span_name))", + "expr": "sum by (callName) (rate(connector_middleware_stack_sum[$__rate_interval])/rate(connector_middleware_stack_count[$__rate_interval]))", + "hide": false, "instant": false, "legendFormat": "__auto", "range": true, - "refId": "A" - } - ], - "title": "Graphql Resolver Duration (95th Percentile)", - "type": "bargauge" - }, - { - "datasource": { - "type": "tempo", - "uid": "P214B5B846CF3925F" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 17 - }, - "id": 4, - "options": { - "cellHeight": "sm", - "footer": { - "countRows": false, - "fields": "", - "reducer": [ - "sum" - ], - "show": false + "refId": "B" }, - "showHeader": true, - "sortBy": [ - { - "desc": true, - "displayName": "Duration" - } - ] - }, - "pluginVersion": "11.2.0", - "targets": [ { "datasource": { - "type": "tempo", - "uid": "P214B5B846CF3925F" + "type": "prometheus", + "uid": "${PrometheusDS}" }, - "filters": [ - { - "id": "9bab4a0a", - "operator": "=", - "scope": "span" - }, - { - "id": "service-name", - "operator": "=", - "scope": "resource", - "tag": "service.name", - "value": [ - "RAFIKI_NETWORK" - ], - "valueType": "string" - }, - { - "id": "span-name", - "operator": "=", - "scope": "span", - "tag": "name", - "value": [], - "valueType": "string" - }, - { - "id": "min-duration", - "operator": ">", - "tag": "duration", - "value": "100ms", - "valueType": "duration" - } - ], - "limit": 20, - "queryType": "traceqlSearch", - "refId": "A", - "tableType": "traces" + "editorMode": "code", + "expr": "sum by (callName) (rate(balanceMiddleware_sum[$__rate_interval])/rate(balanceMiddleware_count[$__rate_interval]))", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "C" } ], - "title": "Traces > 100ms", - "type": "table" + "title": "ILP Connector", + "type": "timeseries" }, { "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -1109,7 +1491,7 @@ "h": 8, "w": 12, "x": 0, - "y": 29 + "y": 44 }, "id": 2, "interval": "15s", @@ -1129,7 +1511,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "(increase(packet_amount_fulfill_total[30s]) * 0.5 * 0.0001)", @@ -1146,8 +1528,9 @@ }, { "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "fieldConfig": { "defaults": { @@ -1161,6 +1544,7 @@ "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, + "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", @@ -1191,7 +1575,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1206,7 +1591,7 @@ "h": 8, "w": 12, "x": 0, - "y": 37 + "y": 52 }, "id": 1, "interval": "15s", @@ -1226,7 +1611,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "sum by (source) (round(increase(transactions_total[30s]) * 0.5))", @@ -1243,8 +1628,9 @@ }, { "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "description": "Maximum time it takes to complete the fastest 25%/50%/75% of ILP payments.", "fieldConfig": { @@ -1257,7 +1643,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1273,7 +1660,7 @@ "h": 5, "w": 12, "x": 0, - "y": 45 + "y": 60 }, "id": 3, "options": { @@ -1283,9 +1670,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1293,12 +1678,12 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.4", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "histogram_quantile(0.25, ilp_pay_time_ms_bucket)", @@ -1310,7 +1695,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "histogram_quantile(0.50, ilp_pay_time_ms_bucket)", @@ -1323,7 +1708,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "histogram_quantile(0.75, ilp_pay_time_ms_bucket)", @@ -1339,8 +1724,9 @@ }, { "datasource": { + "name": "${PrometheusDS}", "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "description": "Maximum time it takes to complete the fastest 25%/50%/75% of ILP quotes.", "fieldConfig": { @@ -1353,7 +1739,8 @@ "mode": "absolute", "steps": [ { - "color": "green" + "color": "green", + "value": null }, { "color": "red", @@ -1369,7 +1756,7 @@ "h": 5, "w": 12, "x": 0, - "y": 50 + "y": 65 }, "id": 5, "options": { @@ -1379,9 +1766,7 @@ "orientation": "auto", "percentChangeColorMode": "standard", "reduceOptions": { - "calcs": [ - "lastNotNull" - ], + "calcs": ["lastNotNull"], "fields": "", "values": false }, @@ -1389,12 +1774,12 @@ "textMode": "auto", "wideLayout": true }, - "pluginVersion": "11.1.4", + "pluginVersion": "11.2.0", "targets": [ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "histogram_quantile(0.25, ilp_rate_probe_time_ms_bucket)", @@ -1406,7 +1791,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "histogram_quantile(0.50, ilp_rate_probe_time_ms_bucket)", @@ -1419,7 +1804,7 @@ { "datasource": { "type": "prometheus", - "uid": "PBFA97CFB590B2093" + "uid": "${PrometheusDS}" }, "editorMode": "code", "expr": "histogram_quantile(0.75, ilp_rate_probe_time_ms_bucket)", @@ -1438,10 +1823,48 @@ "schemaVersion": 39, "tags": [], "templating": { - "list": [] + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "PBFA97CFB590B2093" + }, + "description": "The Prometheus data-source.", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "PrometheusDS", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Tempo", + "value": "P214B5B846CF3925F" + }, + "description": "The Tempo datasource uid.", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "TempoDS", + "options": [], + "query": "tempo", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] }, "time": { - "from": "now-15m", + "from": "now-5m", "to": "now" }, "timepicker": { @@ -1462,4 +1885,4 @@ "uid": "fdr58stwkr6yof", "version": 1, "weekStart": "" -} \ No newline at end of file +} diff --git a/localenv/telemetry/grafana/provisioning/dashboards/jason_example.json b/localenv/telemetry/grafana/provisioning/dashboards/jason_example.json new file mode 100644 index 0000000000..22bd6fc247 --- /dev/null +++ b/localenv/telemetry/grafana/provisioning/dashboards/jason_example.json @@ -0,0 +1,1262 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_time_ms_sum[$__rate_interval])/rate(quote_service_create_time_ms_count[$__rate_interval])", + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_resolve_receiver_time_ms_sum[$__rate_interval])/rate(quote_service_create_resolve_receiver_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_get_quote_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_quote_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_get_latest_fee_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_latest_fee_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_insert_time_ms_sum[$__rate_interval])/rate(quote_service_create_insert_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_finalize_quote_ms_sum[$__rate_interval])/rate(quote_service_finalize_quote_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "F" + } + ], + "title": "Quote Service Create Time (ms)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_get_quote_time_ms_sum[$__rate_interval])/rate(quote_service_create_get_quote_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "C" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(ilp_rate_probe_time_ms_sum[$__rate_interval])/rate(ilp_rate_probe_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "F" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(ilp_get_quote_rate_time_ms_sum[$__rate_interval])/rate(ilp_get_quote_rate_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(ilp_make_ilp_plugin_connect_time_ms_sum[$__rate_interval])/rate(ilp_make_ilp_plugin_connect_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(ilp_plugin_close_connect_time_ms_sum[$__rate_interval])/rate(ilp_plugin_close_connect_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(ilp_plugin_disconnect_time_ms_sum[$__rate_interval])/rate(ilp_plugin_disconnect_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "E" + } + ], + "title": "ILP Payment Service getQuote", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_create_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_create_time_ms_count[$__rate_interval])", + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_create_quote_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_create_quote_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_getwalletaddress_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_getwalletaddress_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "E" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_insertgrant_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_insertgrant_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "D" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_insertpayment_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_insertpayment_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "F" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_getreceiver_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_getreceiver_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "H" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_getpeer_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_getpeer_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "G" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_patchpeer_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_patchpeer_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "I" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(outgoing_payment_service_webhook_event_time_ms_sum[$__rate_interval])/rate(outgoing_payment_service_webhook_event_time_ms_count[$__rate_interval])", + "hide": false, + "instant": false, + "legendFormat": "{{description}}", + "range": true, + "refId": "C" + } + ], + "title": "Outgoing Payment Service Create Time (ms)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(quote_service_create_time_ms_sum[$__rate_interval])/rate(quote_service_create_time_ms_sum[$__rate_interval])", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Quote Service Create Time (ms)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 2, + "interval": "15s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "(increase(packet_amount_fulfill_total[30s]) * 0.5 * 0.0001)", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transaction Amount", + "type": "timeseries" + }, + { + "datasource": { + "type": "tempo", + "uid": "P214B5B846CF3925F" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "cellOptions": { + "type": "auto" + }, + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "id": 4, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": ["sum"], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": true, + "displayName": "Duration" + } + ] + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "P214B5B846CF3925F" + }, + "filters": [ + { + "id": "9bab4a0a", + "operator": "=", + "scope": "span" + }, + { + "id": "service-name", + "operator": "=", + "scope": "resource", + "tag": "service.name", + "value": ["RAFIKI_NETWORK"], + "valueType": "string" + }, + { + "id": "span-name", + "operator": "=", + "scope": "span", + "tag": "name", + "value": [], + "valueType": "string" + }, + { + "id": "min-duration", + "operator": ">", + "tag": "duration", + "value": "100ms", + "valueType": "duration" + } + ], + "limit": 20, + "queryType": "traceqlSearch", + "refId": "A", + "tableType": "traces" + } + ], + "title": "Traces > 100ms", + "type": "table" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "id": 1, + "interval": "15s", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum by (source) (round(increase(transactions_total[30s]) * 0.5))", + "hide": false, + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transaction Count", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "continuous-GrYlRd" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 12, + "y": 24 + }, + "id": 6, + "options": { + "displayMode": "gradient", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "horizontal", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.95, sum(rate(traces_spanmetrics_latency_bucket{span_name=~\"^(mutation|query).*\"}[$__rate_interval])) by (le, span_name))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Graphql Resolver Duration (95th Percentile)", + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Maximum time it takes to complete the fastest 25%/50%/75% of ILP payments.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.25, ilp_pay_time_ms_bucket)", + "instant": false, + "legendFormat": "First Quartile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, ilp_pay_time_ms_bucket)", + "hide": false, + "instant": false, + "legendFormat": "Second Quartile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.75, ilp_pay_time_ms_bucket)", + "hide": false, + "instant": false, + "legendFormat": "Third Quartile", + "range": true, + "refId": "C" + } + ], + "title": "ILP Pay Time Quartile Approximations", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Maximum time it takes to complete the fastest 25%/50%/75% of ILP quotes.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 37 + }, + "id": 5, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.25, ilp_rate_probe_time_ms_bucket)", + "instant": false, + "legendFormat": "First Quartile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, ilp_rate_probe_time_ms_bucket)", + "hide": false, + "instant": false, + "legendFormat": "Second Quartile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.75, ilp_rate_probe_time_ms_bucket)", + "hide": false, + "instant": false, + "legendFormat": "Third Quartile", + "range": true, + "refId": "C" + } + ], + "title": "ILP Quote Time Quartile Approximations", + "type": "stat" + } + ], + "refresh": "15s", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "PBFA97CFB590B2093" + }, + "description": "The Prometheus data-source.", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "PrometheusDS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": { + "selected": false, + "text": "Tempo", + "value": "P214B5B846CF3925F" + }, + "description": "The Tempo datasource uid.", + "hide": 0, + "includeAll": false, + "multi": false, + "name": "TempoDS", + "options": [], + "query": "tempo", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "15s", + "30s", + "1m", + "2m", + "5m", + "10m", + "15m", + "30m", + "1h" + ] + }, + "timezone": "browser", + "title": "Jason - Example Dashboard", + "uid": "fdr58stwkr6yoe", + "version": 1, + "weekStart": "" +} diff --git a/localenv/telemetry/grafana/provisioning/dashboards/local_example.json b/localenv/telemetry/grafana/provisioning/dashboards/local_example.json new file mode 100644 index 0000000000..f08e921f6f --- /dev/null +++ b/localenv/telemetry/grafana/provisioning/dashboards/local_example.json @@ -0,0 +1,1894 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 12, + "interval": "15", + "maxDataPoints": 120, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (source) (increase(transactions_total[$__interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (sum by(source) (increase(transactions_total[$__interval])))", + "hide": false, + "instant": false, + "legendFormat": "TOTAL", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1h", + "title": "Transaction Count (by source)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 11, + "interval": "15", + "maxDataPoints": 120, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "(increase(transactions_amount_total[$__interval])* 0.0001) ", + "hide": true, + "instant": false, + "legendFormat": "{{asset_code}} - scale: 4", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (increase(transactions_amount_total[$__interval])* 0.0001 ) ", + "hide": false, + "instant": false, + "legendFormat": "Total ", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1h", + "title": "Value Sent Through Network", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 7, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 3, + "interval": "30", + "maxDataPoints": 120, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum (increase(transactions_amount_total[$__interval])) / sum (increase(transactions_total[$__interval])) * 0.0001", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "1h", + "title": "Transaction Average Amount", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 7 + }, + "id": 1, + "interval": "30s", + "maxDataPoints": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (source) (increase(transactions_total[$__interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (sum by(source) (increase(transactions_total[$__interval])))", + "hide": false, + "instant": false, + "legendFormat": "TOTAL", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1d", + "title": "Transaction Count (by source)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 7 + }, + "id": 2, + "interval": "30s", + "maxDataPoints": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "(increase(transactions_amount_total[$__interval])* 0.0001) ", + "hide": true, + "instant": false, + "legendFormat": "{{asset_code}} - scale: 4", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (increase(transactions_amount_total[$__interval])* 0.0001 ) ", + "hide": false, + "instant": false, + "legendFormat": "Total ", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1d", + "title": "Value Sent Through Network", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 7, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 7 + }, + "id": 13, + "interval": "30s", + "maxDataPoints": 24, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum (increase(transactions_amount_total[$__interval])) / sum (increase(transactions_total[$__interval])) * 0.0001", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "1d", + "title": "Transaction Average Amount", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 14 + }, + "id": 4, + "interval": "30s", + "maxDataPoints": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (source) (increase(transactions_total[$__interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (sum by(source) (increase(transactions_total[$__interval])))", + "hide": false, + "instant": false, + "legendFormat": "TOTAL", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1w", + "title": "Transaction Count (by source)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 14 + }, + "id": 6, + "interval": "30s", + "maxDataPoints": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "(increase(transactions_amount_total[$__interval])* 0.0001) ", + "hide": true, + "instant": false, + "legendFormat": "{{asset_code}} - scale: 4", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (increase(transactions_amount_total[$__interval])* 0.0001 ) ", + "hide": false, + "instant": false, + "legendFormat": "Total ", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1w", + "title": "Value Sent Through Network", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 7, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 14 + }, + "id": 8, + "interval": "30s", + "maxDataPoints": 7, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum (increase(transactions_amount_total[$__interval])) / sum (increase(transactions_total[$__interval])) * 0.0001", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "timeFrom": "1w", + "title": "Transaction Average Amount", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 21 + }, + "id": 5, + "interval": "30s", + "maxDataPoints": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by (source) (increase(transactions_total[$__interval]))", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (sum by(source) (increase(transactions_total[$__interval])))", + "hide": false, + "instant": false, + "legendFormat": "TOTAL", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1M", + "title": "Transaction Count (by source)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 5, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 21 + }, + "id": 7, + "interval": "30s", + "maxDataPoints": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "(increase(transactions_amount_total[$__interval])* 0.0001) ", + "hide": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum by(job) (increase(transactions_amount_total[$__interval])* 0.0001 ) ", + "hide": false, + "instant": false, + "legendFormat": "Total ", + "range": true, + "refId": "B" + } + ], + "timeFrom": "1M", + "title": "Value Sent Through Network", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 7, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 3, + "pointSize": 10, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 4, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 21 + }, + "id": 9, + "interval": "30s", + "maxDataPoints": 31, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "11.3.0-74868", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum (increase(transactions_amount_total[$__interval])) / sum (increase(transactions_total[$__interval])) * 0.0001", + "instant": false, + "legendFormat": "Transaction average amount", + "range": true, + "refId": "A" + } + ], + "timeFrom": "1M", + "title": "Transaction Average Amount", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "Distribution of the time in milliseconds it takes to complete an ILP payment.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 28 + }, + "id": 14, + "options": { + "displayMode": "gradient", + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "ilp_pay_time_ms_bucket", + "format": "heatmap", + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transaction Time Distribution", + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "The average time in milliseconds to complete an ILP payment", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 28 + }, + "id": 15, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "ilp_pay_time_ms_sum/ilp_pay_time_ms_count", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Average Transaction Time", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "Percent of ILP payments that completed in less than or equal to 50ms", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 28 + }, + "id": 16, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "ilp_pay_time_ms_bucket{le=\"50\"} / ignoring (le) ilp_pay_time_ms_count ## not sure if 50 is best. should be whatever our target is. if we dont have one then can probably delete this visualization", + "format": "time_series", + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Shorter Than 500m", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "Maximum time it takes to complete the fastest 25%/50%/75% of ILP payments.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 12, + "y": 32 + }, + "id": 18, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "text": {}, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.25, ilp_pay_time_ms_bucket)", + "format": "time_series", + "instant": false, + "legendFormat": "First Quartile", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.50, ilp_pay_time_ms_bucket)", + "hide": false, + "instant": false, + "legendFormat": "Second Quartile", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "histogram_quantile(0.75, ilp_pay_time_ms_bucket)", + "hide": false, + "instant": false, + "legendFormat": "Third Quartile", + "range": true, + "refId": "C" + } + ], + "title": "Quartile Approximations", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "Distribution over time of the time in milliseconds it takes to complete an ILP payment.", + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 12, + "x": 0, + "y": 36 + }, + "id": 17, + "maxDataPoints": 25, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "mode": "single", + "showColorScale": false, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "11.1.4", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(increase(ilp_pay_time_ms_bucket[$__interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Transaction Time Distribution Over Time", + "type": "heatmap" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 8, + "x": 12, + "y": 37 + }, + "id": 19, + "interval": "15", + "maxDataPoints": 592, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "editorMode": "code", + "expr": "sum(rate(transactions_total[5m]))", + "instant": false, + "legendFormat": "Number of transactions completed", + "range": true, + "refId": "A" + } + ], + "title": "Total Transactions Per Second", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "${PrometheusDS}" + }, + "definition": "label_values(transactions_amount_total, asset_code)", + "hide": 0, + "includeAll": true, + "label": "asset_code", + "multi": false, + "name": "asset_code", + "options": [], + "query": { + "qryType": 5, + "query": "label_values(transactions_amount_total, asset_code)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "PBFA97CFB590B2093" + }, + "hide": 0, + "includeAll": false, + "multi": false, + "name": "PrometheusDS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TESTNET Network Insights Local", + "uid": "edvzhzt3k47pca", + "version": 4, + "weekStart": "" +} diff --git a/localenv/telemetry/otel-collector-config.yaml b/localenv/telemetry/otel-collector-config.yaml index 32e08f8902..d577a0ec78 100644 --- a/localenv/telemetry/otel-collector-config.yaml +++ b/localenv/telemetry/otel-collector-config.yaml @@ -8,6 +8,8 @@ processors: batch: exporters: + logging: + loglevel: info debug: verbosity: detailed prometheus: @@ -18,6 +20,9 @@ exporters: insecure: true service: + telemetry: + logs: + level: warn pipelines: metrics: receivers: [otlp] diff --git a/packages/backend/src/accounting/psql/balance.ts b/packages/backend/src/accounting/psql/balance.ts index 8edd836c0d..b057f0a85f 100644 --- a/packages/backend/src/accounting/psql/balance.ts +++ b/packages/backend/src/accounting/psql/balance.ts @@ -14,10 +14,9 @@ export async function getAccountBalances( account: LedgerAccount, trx?: TransactionOrKnex ): Promise { - deps.telemetry && - deps.telemetry.startTimer('psql_getAccountBalances', { - callName: 'psql_getAccountBalances' - }) + const stopTimer = deps.telemetry?.startTimer('psql_getAccountBalances', { + callName: 'psql_getAccountBalances' + }) try { const queryResult = await (trx ?? deps.knex).raw( ` @@ -42,7 +41,7 @@ export async function getAccountBalances( const debitsPosted = BigInt(queryResult.rows[0].debitsPosted) const debitsPending = BigInt(queryResult.rows[0].debitsPending) - deps.telemetry && deps.telemetry.stopTimer('psql_getAccountBalances') + stopTimer && stopTimer() return { creditsPosted, diff --git a/packages/backend/src/accounting/psql/service.ts b/packages/backend/src/accounting/psql/service.ts index ecdf64f177..7470064ca9 100644 --- a/packages/backend/src/accounting/psql/service.ts +++ b/packages/backend/src/accounting/psql/service.ts @@ -35,8 +35,10 @@ import { } from './ledger-transfer' import { LedgerTransfer, LedgerTransferType } from './ledger-transfer/model' import { TelemetryService } from '../../telemetry/service' +import { AssetService } from '../../asset/service' export interface ServiceDependencies extends BaseService { + fetchAssetService?: () => Promise knex: TransactionOrKnex withdrawalThrottleDelay?: number telemetry?: TelemetryService @@ -147,26 +149,19 @@ export async function getAccountTotalSent( deps: ServiceDependencies, accountRef: string ): Promise { - deps.telemetry && - deps.telemetry.startTimer('psql_getAccountTotalSent', { - callName: 'psql_getAccountTotalSent' - }) + const stopTimer = deps.telemetry?.startTimer('psql_getAccountTotalSent', { + callName: 'psql_getAccountTotalSent' + }) const account = await getLiquidityAccount(deps, accountRef) if (!account) { - deps.telemetry && - deps.telemetry.startTimer('psql_getAccountTotalSent', { - callName: 'psql_getAccountTotalSent' - }) + stopTimer && stopTimer() return } const totalsSent = (await getAccountBalances(deps, account)).debitsPosted - deps.telemetry && - deps.telemetry.startTimer('psql_getAccountTotalSent', { - callName: 'psql_getAccountTotalSent' - }) + stopTimer && stopTimer() return totalsSent } diff --git a/packages/backend/src/accounting/service.ts b/packages/backend/src/accounting/service.ts index bdbbc3c2fc..1a5b063267 100644 --- a/packages/backend/src/accounting/service.ts +++ b/packages/backend/src/accounting/service.ts @@ -1,6 +1,7 @@ import { TransactionOrKnex } from 'objection' import { BaseService } from '../shared/baseService' import { TransferError, isTransferError } from './errors' +import { AssetService } from '../asset/service' export enum LiquidityAccountType { ASSET = 'ASSET', @@ -40,6 +41,8 @@ export interface LiquidityAccount { export interface OnCreditOptions { totalReceived: bigint withdrawalThrottleDelay?: number + // TODO: make this required. should only need updating the tests to do this. + fetchAssetService?: () => Promise } export interface OnDebitOptions { @@ -133,6 +136,7 @@ export interface TransferToCreate { } export interface BaseAccountingServiceDependencies extends BaseService { + fetchAssetService?: () => Promise withdrawalThrottleDelay?: number } @@ -160,7 +164,7 @@ export async function createAccountToAccountTransfer( getAccountBalance } = args - const { withdrawalThrottleDelay } = deps + const { withdrawalThrottleDelay, fetchAssetService } = deps const { sourceAccount, destinationAccount, sourceAmount, destinationAmount } = transferArgs @@ -226,7 +230,8 @@ export async function createAccountToAccountTransfer( await destinationAccount.onCredit({ totalReceived, - withdrawalThrottleDelay + withdrawalThrottleDelay, + fetchAssetService }) } }, diff --git a/packages/backend/src/accounting/tigerbeetle/service.ts b/packages/backend/src/accounting/tigerbeetle/service.ts index 479bb754d5..88b98a8b3e 100644 --- a/packages/backend/src/accounting/tigerbeetle/service.ts +++ b/packages/backend/src/accounting/tigerbeetle/service.ts @@ -32,6 +32,7 @@ import { } from './transfers' import { toTigerBeetleId } from './utils' import { TelemetryService } from '../../telemetry/service' +import { AssetService } from '../../asset/service' export enum TigerBeetleAccountCode { LIQUIDITY_WEB_MONETIZATION = 1, @@ -68,6 +69,7 @@ export const convertToTigerBeetleTransferCode: { } export interface ServiceDependencies extends BaseService { + fetchAssetService?: () => Promise tigerBeetle: Client withdrawalThrottleDelay?: number telemetry?: TelemetryService @@ -220,16 +222,15 @@ export async function getAccountTotalSent( deps: ServiceDependencies, id: string ): Promise { - deps.telemetry && - deps.telemetry.startTimer('tb_getAccountTotalSent', { - callName: 'tb_getAccountTotalSent' - }) + const stopTimer = deps.telemetry?.startTimer('tb_getAccountTotalSent', { + callName: 'tb_getAccountTotalSent' + }) const account = (await getAccounts(deps, [id]))[0] if (account) { - deps.telemetry && deps.telemetry.stopTimer('tb_getAccountTotalSent') + stopTimer && stopTimer() return account.debits_posted } - deps.telemetry && deps.telemetry.stopTimer('tb_getAccountTotalSent') + stopTimer && stopTimer() } export async function getAccountsTotalSent( diff --git a/packages/backend/src/asset/service.ts b/packages/backend/src/asset/service.ts index 139006e926..ee41024975 100644 --- a/packages/backend/src/asset/service.ts +++ b/packages/backend/src/asset/service.ts @@ -7,6 +7,9 @@ import { BaseService } from '../shared/baseService' import { AccountingService, LiquidityAccountType } from '../accounting/service' import { WalletAddress } from '../open_payments/wallet_address/model' import { Peer } from '../payment-method/ilp/peer/model' +import { Quote } from '../open_payments/quote/model' +import { IncomingPayment } from '../open_payments/payment/incoming/model' +import { CacheDataStore } from '../cache' export interface AssetOptions { code: string @@ -28,37 +31,46 @@ export interface DeleteOptions { deletedAt: Date } +export type ToSetOn = Quote | IncomingPayment | WalletAddress | Peer | undefined + export interface AssetService { create(options: CreateOptions): Promise update(options: UpdateOptions): Promise delete(options: DeleteOptions): Promise get(id: string): Promise + setOn(obj: ToSetOn): Promise getPage(pagination?: Pagination, sortOrder?: SortOrder): Promise getAll(): Promise } interface ServiceDependencies extends BaseService { accountingService: AccountingService + cacheDataStore: CacheDataStore } export async function createAssetService({ logger, knex, - accountingService + accountingService, + cacheDataStore }: ServiceDependencies): Promise { const log = logger.child({ service: 'AssetService' }) + const deps: ServiceDependencies = { logger: log, knex, - accountingService + accountingService, + cacheDataStore } + return { create: (options) => createAsset(deps, options), update: (options) => updateAsset(deps, options), delete: (options) => deleteAsset(deps, options), get: (id) => getAsset(deps, id), + setOn: (toSetOn) => setAssetOn(deps, toSetOn), getPage: (pagination?, sortOrder?) => getAssetsPage(deps, pagination, sortOrder), getAll: () => getAll(deps) @@ -78,6 +90,8 @@ async function createAsset( .first() if (deletedAsset) { + await deps.cacheDataStore.delete(deletedAsset.id) + // if found, enable return await Asset.query(deps.knex) .patchAndFetchById(deletedAsset.id, { deletedAt: null }) @@ -120,9 +134,12 @@ async function updateAsset( throw new Error('Knex undefined') } try { - return await Asset.query(deps.knex) + const returnVal = await Asset.query(deps.knex) .patchAndFetchById(id, { withdrawalThreshold, liquidityThreshold }) .throwIfNotFound() + + await deps.cacheDataStore.delete(id) + return returnVal } catch (err) { if (err instanceof NotFoundError) { return AssetError.UnknownAsset @@ -139,6 +156,8 @@ async function deleteAsset( if (!deps.knex) { throw new Error('Knex undefined') } + + await deps.cacheDataStore.delete(id) try { // return error in case there is a peer or wallet address using the asset const peer = await Peer.query(deps.knex).where('assetId', id).first() @@ -152,7 +171,6 @@ async function deleteAsset( if (walletAddress) { return AssetError.CannotDeleteInUseAsset } - return await Asset.query(deps.knex) .patchAndFetchById(id, { deletedAt: deletedAt.toISOString() }) .throwIfNotFound() @@ -168,7 +186,13 @@ async function getAsset( deps: ServiceDependencies, id: string ): Promise { - return await Asset.query(deps.knex).whereNull('deletedAt').findById(id) + const inMem = (await deps.cacheDataStore.get(id)) as Asset + if (inMem) return inMem + + const asset = await Asset.query(deps.knex).whereNull('deletedAt').findById(id) + if (asset) await deps.cacheDataStore.set(asset.id, asset) + + return asset } async function getAssetsPage( @@ -176,11 +200,21 @@ async function getAssetsPage( pagination?: Pagination, sortOrder?: SortOrder ): Promise { - return await Asset.query(deps.knex) + return Asset.query(deps.knex) .whereNull('deletedAt') .getPage(pagination, sortOrder) } async function getAll(deps: ServiceDependencies): Promise { - return await Asset.query(deps.knex).whereNull('deletedAt') + return Asset.query(deps.knex).whereNull('deletedAt') +} + +async function setAssetOn( + deps: ServiceDependencies, + obj: ToSetOn +): Promise { + if (!obj) return + const asset = await getAsset(deps, obj.assetId) + if (asset) obj.asset = asset + return asset } diff --git a/packages/backend/src/cache/cache.ts b/packages/backend/src/cache/cache.ts new file mode 100644 index 0000000000..fa2d599785 --- /dev/null +++ b/packages/backend/src/cache/cache.ts @@ -0,0 +1,46 @@ +import { CacheDataStore, CacheSupport } from './index' + +export interface Cached { + expiry: number + data: CacheSupport +} + +export function createInMemoryDataStore(keyTtlMs: number): CacheDataStore { + const map = new Map() + + const getAndCheckExpiry = (key: string): Cached | undefined => { + const cached = map.get(key) + if (cached?.expiry && Date.now() >= cached.expiry) { + deleteKey(key) + return undefined + } + return cached + } + + const deleteKey = (key: string) => map.delete(key) + + return { + async get(key: string): Promise { + if (keyTtlMs < 1) return undefined + const cached = getAndCheckExpiry(key) + return cached?.data + }, + async getKeyExpiry(key: string): Promise { + const cached = getAndCheckExpiry(key) + + return cached ? new Date(cached.expiry) : undefined + }, + async delete(key: string): Promise { + deleteKey(key) + }, + async set(key: string, value: CacheSupport): Promise { + if (keyTtlMs < 1) return true + map.set(key, { expiry: Date.now() + keyTtlMs, data: value }) + return true + }, + async deleteAll(): Promise { + map.clear() + } + } +} +export { CacheDataStore } diff --git a/packages/backend/src/cache/index.ts b/packages/backend/src/cache/index.ts new file mode 100644 index 0000000000..0aa89227c2 --- /dev/null +++ b/packages/backend/src/cache/index.ts @@ -0,0 +1,19 @@ +import { Quote } from '../open_payments/quote/model' +import { WalletAddress } from '../open_payments/wallet_address/model' +import { OutgoingPayment } from '../open_payments/payment/outgoing/model' +import { Asset } from '../asset/model' + +export type CacheSupport = + | Quote + | WalletAddress + | OutgoingPayment + | Asset + | undefined + +export interface CacheDataStore { + get(key: string): Promise + getKeyExpiry(key: string): Promise + set(key: string, value: CacheSupport): Promise + delete(key: string): Promise + deleteAll(): Promise +} diff --git a/packages/backend/src/config/app.ts b/packages/backend/src/config/app.ts index 9368e43a82..c17b2f70e5 100644 --- a/packages/backend/src/config/app.ts +++ b/packages/backend/src/config/app.ts @@ -187,7 +187,8 @@ export const Config = { 'INCOMING_PAYMENT_EXPIRY_MAX_MS', 2592000000 ), // 30 days - enableSpspPaymentPointers: envBool('ENABLE_SPSP_PAYMENT_POINTERS', true) + enableSpspPaymentPointers: envBool('ENABLE_SPSP_PAYMENT_POINTERS', true), + localCacheDuration: envInt('LOCAL_CACHE_DURATION', 5000) } function parseRedisTlsConfig( diff --git a/packages/backend/src/graphql/resolvers/liquidity.test.ts b/packages/backend/src/graphql/resolvers/liquidity.test.ts index e0968b27e1..8fc50966c4 100644 --- a/packages/backend/src/graphql/resolvers/liquidity.test.ts +++ b/packages/backend/src/graphql/resolvers/liquidity.test.ts @@ -53,7 +53,10 @@ describe('Liquidity Resolvers', (): void => { const timeoutTwoPhase = 10 beforeAll(async (): Promise => { - deps = await initIocContainer(Config) + deps = initIocContainer({ + ...Config, + localCacheDuration: 0 + }) appContainer = await createTestApp(deps) knex = appContainer.knex accountingService = await deps.use('accountingService') diff --git a/packages/backend/src/graphql/resolvers/liquidity.ts b/packages/backend/src/graphql/resolvers/liquidity.ts index ba434e31ea..ae87d9f650 100644 --- a/packages/backend/src/graphql/resolvers/liquidity.ts +++ b/packages/backend/src/graphql/resolvers/liquidity.ts @@ -33,6 +33,7 @@ import { errorToCode as peerErrorToCode } from '../../payment-method/ilp/peer/errors' import { IncomingPaymentEventType } from '../../open_payments/payment/incoming/model' +import { Config } from '../../config/app' export const getAssetLiquidity: AssetResolvers['liquidity'] = async (parent, args, ctx): Promise => { @@ -440,6 +441,12 @@ export const depositOutgoingPaymentLiquidity: MutationResolvers[' args, ctx ): Promise => { + const telemetry = Config.enableTelemetry + ? await ctx.container.use('telemetry') + : undefined + const stopTimer = telemetry?.startTimer('depositOutgoingPaymentLiquidity', { + callName: 'depositOutgoingPaymentLiquidity' + }) const { outgoingPaymentId } = args.input const webhookService = await ctx.container.use('webhookService') const event = await webhookService.getLatestByResourceId({ @@ -447,6 +454,7 @@ export const depositOutgoingPaymentLiquidity: MutationResolvers[' types: [OutgoingPaymentDepositType.PaymentCreated] }) if (!event || !isOutgoingPaymentEvent(event)) { + stopTimer && stopTimer() throw new GraphQLError(errorToMessage[LiquidityError.InvalidId], { extensions: { code: errorToCode[LiquidityError.InvalidId] @@ -455,6 +463,7 @@ export const depositOutgoingPaymentLiquidity: MutationResolvers[' } if (!event.data.debitAmount) { + stopTimer && stopTimer() throw new Error('No debit amount') } const outgoingPaymentService = await ctx.container.use( @@ -466,12 +475,14 @@ export const depositOutgoingPaymentLiquidity: MutationResolvers[' transferId: event.id }) if (isFundingError(paymentOrErr)) { + stopTimer && stopTimer() throw new GraphQLError(fundingErrorToMessage[paymentOrErr], { extensions: { code: fundingErrorToCode[paymentOrErr] } }) } + stopTimer && stopTimer() return { success: true } diff --git a/packages/backend/src/graphql/resolvers/wallet_address.test.ts b/packages/backend/src/graphql/resolvers/wallet_address.test.ts index ff51dd0d1f..32c0628a0b 100644 --- a/packages/backend/src/graphql/resolvers/wallet_address.test.ts +++ b/packages/backend/src/graphql/resolvers/wallet_address.test.ts @@ -43,7 +43,10 @@ describe('Wallet Address Resolvers', (): void => { let walletAddressService: WalletAddressService beforeAll(async (): Promise => { - deps = await initIocContainer(Config) + deps = initIocContainer({ + ...Config, + localCacheDuration: 0 + }) appContainer = await createTestApp(deps) knex = appContainer.knex walletAddressService = await deps.use('walletAddressService') diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 6123bde11e..6ac174ae48 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -56,6 +56,7 @@ import { createStreamCredentialsService } from './payment-method/ilp/stream-cred import { createRatesService } from './rates/service' import { TelemetryService, createTelemetryService } from './telemetry/service' import { createWebhookService } from './webhook/service' +import { createInMemoryDataStore } from './cache/cache' BigInt.prototype.toJSON = function () { return this.toString() @@ -202,9 +203,10 @@ export function initIocContainer( const logger = await deps.use('logger') const knex = await deps.use('knex') return await createAssetService({ - logger: logger, - knex: knex, - accountingService: await deps.use('accountingService') + logger, + knex, + accountingService: await deps.use('accountingService'), + cacheDataStore: createInMemoryDataStore(config.localCacheDuration) }) }) @@ -232,7 +234,8 @@ export function initIocContainer( knex, tigerBeetle, withdrawalThrottleDelay: config.withdrawalThrottleDelay, - telemetry + telemetry, + fetchAssetService: () => Promise.resolve(deps.use('assetService')) }) } @@ -240,7 +243,8 @@ export function initIocContainer( logger, knex, withdrawalThrottleDelay: config.withdrawalThrottleDelay, - telemetry + telemetry, + fetchAssetService: () => Promise.resolve(deps.use('assetService')) }) }) container.singleton('peerService', async (deps) => { @@ -280,7 +284,9 @@ export function initIocContainer( knex: await deps.use('knex'), logger: logger, accountingService: await deps.use('accountingService'), - webhookService: await deps.use('webhookService') + webhookService: await deps.use('webhookService'), + assetService: await deps.use('assetService'), + cacheDataStore: createInMemoryDataStore(config.localCacheDuration) }) }) container.singleton('spspRoutes', async (deps) => { @@ -298,7 +304,8 @@ export function initIocContainer( knex: await deps.use('knex'), accountingService: await deps.use('accountingService'), walletAddressService: await deps.use('walletAddressService'), - config: await deps.use('config') + config: await deps.use('config'), + assetService: await deps.use('assetService') }) }) container.singleton('remoteIncomingPaymentService', async (deps) => { @@ -469,7 +476,9 @@ export function initIocContainer( ), telemetry: config.enableTelemetry ? await deps.use('telemetry') - : undefined + : undefined, + assetService: await deps.use('assetService'), + cacheDataStore: createInMemoryDataStore(config.localCacheDuration) }) }) @@ -496,7 +505,9 @@ export function initIocContainer( quoteService: await deps.use('quoteService'), telemetry: config.enableTelemetry ? await deps.use('telemetry') - : undefined + : undefined, + assetService: await deps.use('assetService'), + cacheDataStore: createInMemoryDataStore(config.localCacheDuration) }) }) diff --git a/packages/backend/src/open_payments/payment/incoming/service.test.ts b/packages/backend/src/open_payments/payment/incoming/service.test.ts index 5b2e98df1e..141fc41781 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.test.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.test.ts @@ -40,7 +40,10 @@ describe('Incoming Payment Service', (): void => { let config: IAppConfig beforeAll(async (): Promise => { - deps = await initIocContainer(Config) + deps = initIocContainer({ + ...Config, + localCacheDuration: 0 + }) appContainer = await createTestApp(deps) accountingService = await deps.use('accountingService') knex = appContainer.knex diff --git a/packages/backend/src/open_payments/payment/incoming/service.ts b/packages/backend/src/open_payments/payment/incoming/service.ts index 6d7bd48e0a..88a01c02d4 100644 --- a/packages/backend/src/open_payments/payment/incoming/service.ts +++ b/packages/backend/src/open_payments/payment/incoming/service.ts @@ -18,6 +18,7 @@ import { Amount } from '../../amount' import { IncomingPaymentError } from './errors' import { IAppConfig } from '../../../config/app' import { poll } from '../../../shared/utils' +import { AssetService } from '../../../asset/service' export const POSITIVE_SLIPPAGE = BigInt(1) // First retry waits 10 seconds @@ -49,6 +50,7 @@ export interface ServiceDependencies extends BaseService { knex: TransactionOrKnex accountingService: AccountingService walletAddressService: WalletAddressService + assetService: AssetService config: IAppConfig } @@ -68,7 +70,7 @@ export async function createIncomingPaymentService( approve: (id) => approveIncomingPayment(deps, id), cancel: (id) => cancelIncomingPayment(deps, id), complete: (id) => completeIncomingPayment(deps, id), - getWalletAddressPage: (options) => getWalletAddressPage(deps, options), + getWalletAddressPage: (options) => getIncomingPaymentPage(deps, options), processNext: () => processNextIncomingPayment(deps) } } @@ -77,9 +79,10 @@ async function getIncomingPayment( deps: ServiceDependencies, options: GetOptions ): Promise { - const incomingPayment = await IncomingPayment.query(deps.knex) - .get(options) - .withGraphFetched('[asset, walletAddress]') + const incomingPayment = await IncomingPayment.query(deps.knex).get(options) + await deps.assetService.setOn(incomingPayment) + await deps.walletAddressService.setOn(incomingPayment) + if (incomingPayment) return await addReceivedAmount(deps, incomingPayment) else return } @@ -122,18 +125,20 @@ async function createIncomingPayment( } } - let incomingPayment = await IncomingPayment.query(trx || deps.knex) - .insertAndFetch({ - walletAddressId: walletAddressId, - client, - assetId: walletAddress.asset.id, - expiresAt, - incomingAmount, - metadata, - state: IncomingPaymentState.Pending, - processAt: expiresAt - }) - .withGraphFetched('[asset, walletAddress]') + let incomingPayment = await IncomingPayment.query( + trx || deps.knex + ).insertAndFetch({ + walletAddressId: walletAddressId, + client, + assetId: walletAddress.asset.id, + expiresAt, + incomingAmount, + metadata, + state: IncomingPaymentState.Pending, + processAt: expiresAt + }) + await deps.assetService.setOn(incomingPayment) + await deps.walletAddressService.setOn(incomingPayment) await IncomingPaymentEvent.query(trx || deps.knex).insert({ incomingPaymentId: incomingPayment.id, @@ -185,11 +190,13 @@ async function getApprovedOrCanceledIncomingPayment( deps: ServiceDependencies, options: GetOptions ) { - return IncomingPayment.query(deps.knex) + const incomingPayment = await IncomingPayment.query(deps.knex) .get(options) - .withGraphFetched('[asset, walletAddress]') .whereNotNull('approvedAt') .orWhereNotNull('cancelledAt') + await deps.assetService.setOn(incomingPayment) + await deps.walletAddressService.setOn(incomingPayment) + return incomingPayment } // Fetch (and lock) an incoming payment for work. @@ -206,7 +213,11 @@ async function processNextIncomingPayment( // If an incoming payment is locked, don't wait — just come back for it later. .skipLocked() .where('processAt', '<=', now) - .withGraphFetched('[asset, walletAddress]') + + for (const incomingPayment of incomingPayments) { + await deps_.assetService.setOn(incomingPayment) + await deps_.walletAddressService.setOn(incomingPayment) + } const incomingPayment = incomingPayments[0] if (!incomingPayment) return @@ -301,16 +312,21 @@ async function handleDeactivated( } } -async function getWalletAddressPage( +async function getIncomingPaymentPage( deps: ServiceDependencies, options: ListOptions ): Promise { - const page = await IncomingPayment.query(deps.knex) - .list(options) - .withGraphFetched('[asset, walletAddress]') + const page = await IncomingPayment.query(deps.knex).list(options) const amounts = await deps.accountingService.getAccountsTotalReceived( page.map((payment: IncomingPayment) => payment.id) ) + + for (const payment of page) { + await deps.walletAddressService.setOn(payment) + await deps.assetService.setOn(payment.walletAddress) + await deps.assetService.setOn(payment) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types return page.map((payment: IncomingPayment, i: number) => { try { @@ -337,10 +353,9 @@ async function approveIncomingPayment( id: string ): Promise { return deps.knex.transaction(async (trx) => { - const payment = await IncomingPayment.query(trx) - .findById(id) - .forUpdate() - .withGraphFetched('[asset, walletAddress]') + const payment = await IncomingPayment.query(trx).findById(id).forUpdate() + await deps.assetService.setOn(payment) + await deps.walletAddressService.setOn(payment) if (!payment) return IncomingPaymentError.UnknownPayment if (payment.state !== IncomingPaymentState.Pending) @@ -369,10 +384,9 @@ async function cancelIncomingPayment( id: string ): Promise { return deps.knex.transaction(async (trx) => { - const payment = await IncomingPayment.query(trx) - .findById(id) - .forUpdate() - .withGraphFetched('[asset, walletAddress]') + const payment = await IncomingPayment.query(trx).findById(id).forUpdate() + await deps.assetService.setOn(payment) + await deps.walletAddressService.setOn(payment) if (!payment) return IncomingPaymentError.UnknownPayment if (payment.state !== IncomingPaymentState.Pending) @@ -401,11 +415,11 @@ async function completeIncomingPayment( id: string ): Promise { return deps.knex.transaction(async (trx) => { - const payment = await IncomingPayment.query(trx) - .findById(id) - .forUpdate() - .withGraphFetched('[asset, walletAddress]') + const payment = await IncomingPayment.query(trx).findById(id).forUpdate() if (!payment) return IncomingPaymentError.UnknownPayment + + await deps.assetService.setOn(payment) + await deps.walletAddressService.setOn(payment) if ( ![IncomingPaymentState.Pending, IncomingPaymentState.Processing].includes( payment.state @@ -434,6 +448,5 @@ async function addReceivedAmount( assetCode: payment.asset.code, assetScale: payment.asset.scale } - return payment } diff --git a/packages/backend/src/open_payments/payment/outgoing/lifecycle.ts b/packages/backend/src/open_payments/payment/outgoing/lifecycle.ts index ee079d0dc1..ace308bbe3 100644 --- a/packages/backend/src/open_payments/payment/outgoing/lifecycle.ts +++ b/packages/backend/src/open_payments/payment/outgoing/lifecycle.ts @@ -76,24 +76,24 @@ export async function handleSending( throw LifecycleError.BadState } - deps.telemetry && - deps.telemetry.startTimer('ilp_pay_time_ms', { - description: 'Time to complete an ILP payment', - callName: 'paymentMethodHandlerService.pay (ILP)' - }), - await deps.paymentMethodHandlerService.pay('ILP', { - receiver, - outgoingPayment: payment, - finalDebitAmount: maxDebitAmount, - finalReceiveAmount: maxReceiveAmount - }) + const stopTimer = deps.telemetry?.startTimer('ilp_pay_time_ms', { + description: 'Time to complete an ILP payment', + callName: 'paymentMethodHandlerService.pay (ILP)' + }) + + await deps.paymentMethodHandlerService.pay('ILP', { + receiver, + outgoingPayment: payment, + finalDebitAmount: maxDebitAmount, + finalReceiveAmount: maxReceiveAmount + }) + stopTimer && stopTimer() if (deps.telemetry) { await Promise.all([ deps.telemetry.incrementCounter('transactions_total', 1, { description: 'Count of funded transactions' }), - deps.telemetry.stopTimer('ilp_pay_time_ms'), deps.telemetry.incrementCounterWithTransactionAmountDifference( 'transaction_fee_amounts', payment.sentAmount, @@ -144,24 +144,24 @@ export async function handleFailed( payment: OutgoingPayment, error: string ): Promise { - deps.telemetry && - deps.telemetry.startTimer('handleFailed', { callName: 'handleFailed ' }) + const stopTimer = deps.telemetry?.startTimer('handleFailed', { + callName: 'handleFailed' + }) await payment.$query(deps.knex).patch({ state: OutgoingPaymentState.Failed, error }) await sendWebhookEvent(deps, payment, OutgoingPaymentEventType.PaymentFailed) - deps.telemetry && deps.telemetry.stopTimer('handleFailed') + stopTimer && stopTimer() } async function handleCompleted( deps: ServiceDependencies, payment: OutgoingPayment ): Promise { - deps.telemetry && - deps.telemetry.startTimer('handleCompleted', { - callName: 'handleCompleted' - }) + const stopTimer = deps.telemetry?.startTimer('handleCompleted', { + callName: 'handleCompleted' + }) await payment.$query(deps.knex).patch({ state: OutgoingPaymentState.Completed }) @@ -171,7 +171,7 @@ async function handleCompleted( payment, OutgoingPaymentEventType.PaymentCompleted ) - deps.telemetry && deps.telemetry.stopTimer('handleCompleted') + stopTimer && stopTimer() } export async function sendWebhookEvent( @@ -180,10 +180,9 @@ export async function sendWebhookEvent( type: OutgoingPaymentEventType, trx?: TransactionOrKnex ): Promise { - deps.telemetry && - deps.telemetry.startTimer('sendWebhookEvent', { - callName: 'outgoingPaymentLifecycle_sendwebhookEvent' - }) + const stopTimer = deps.telemetry?.startTimer('sendWebhookEvent', { + callName: 'outgoingPaymentLifecycle_sendwebhookEvent' + }) // TigerBeetle accounts are only created as the OutgoingPayment is funded. // So default the amountSent and balance to 0 for outgoing payments still in the funding state const amountSent = @@ -213,7 +212,7 @@ export async function sendWebhookEvent( data: payment.toData({ amountSent, balance }), withdrawal }) - deps.telemetry && deps.telemetry.stopTimer('sendWebhookEvent') + stopTimer && stopTimer() } function validateAssets( diff --git a/packages/backend/src/open_payments/payment/outgoing/service.test.ts b/packages/backend/src/open_payments/payment/outgoing/service.test.ts index 240eefcef6..d17f409186 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.test.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.test.ts @@ -251,7 +251,8 @@ describe('OutgoingPaymentService', (): void => { deps = await initIocContainer({ ...Config, exchangeRatesUrl, - enableTelemetry: true + enableTelemetry: true, + localCacheDuration: 0 }) appContainer = await createTestApp(deps) outgoingPaymentService = await deps.use('outgoingPaymentService') diff --git a/packages/backend/src/open_payments/payment/outgoing/service.ts b/packages/backend/src/open_payments/payment/outgoing/service.ts index 050700aca1..9e143c5e4a 100644 --- a/packages/backend/src/open_payments/payment/outgoing/service.ts +++ b/packages/backend/src/open_payments/payment/outgoing/service.ts @@ -42,6 +42,8 @@ import { QuoteService } from '../../quote/service' import { isQuoteError } from '../../quote/errors' import { Pagination, SortOrder } from '../../../shared/baseModel' import { FilterString } from '../../../shared/filters' +import { AssetService } from '../../../asset/service' +import { CacheDataStore } from '../../../cache' export interface OutgoingPaymentService extends WalletAddressSubresourceService { @@ -66,6 +68,8 @@ export interface ServiceDependencies extends BaseService { paymentMethodHandlerService: PaymentMethodHandlerService walletAddressService: WalletAddressService quoteService: QuoteService + assetService: AssetService + cacheDataStore: CacheDataStore telemetry?: TelemetryService } @@ -83,7 +87,7 @@ export async function createOutgoingPaymentService( cancel: (options) => cancelOutgoingPayment(deps, options), fund: (options) => fundPayment(deps, options), processNext: () => worker.processPendingPayment(deps), - getWalletAddressPage: (options) => getWalletAddressPage(deps, options) + getWalletAddressPage: (options) => getOutgoingPaymentPage(deps, options) } } @@ -105,9 +109,7 @@ async function getOutgoingPaymentsPage( ): Promise { const { filter, pagination, sortOrder } = options ?? {} - const query = OutgoingPayment.query(deps.knex).withGraphFetched( - '[quote.asset, walletAddress]' - ) + const query = OutgoingPayment.query(deps.knex) if (filter?.receiver?.in && filter.receiver.in.length) { query @@ -127,6 +129,13 @@ async function getOutgoingPaymentsPage( const amounts = await deps.accountingService.getAccountsTotalSent( page.map((payment: OutgoingPayment) => payment.id) ) + for (const payment of page) { + await deps.walletAddressService.setOn(payment) + await deps.assetService.setOn(payment.walletAddress) + await deps.quoteService.setOn(payment) + await deps.assetService.setOn(payment.quote) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types return page.map((payment: OutgoingPayment, i: number) => { payment.sentAmount = { @@ -142,11 +151,11 @@ async function getOutgoingPayment( deps: ServiceDependencies, options: GetOptions ): Promise { - const outgoingPayment = await OutgoingPayment.query(deps.knex) - .get(options) - .withGraphFetched('[quote.asset, walletAddress]') - + const outgoingPayment = await OutgoingPayment.query(deps.knex).get(options) if (outgoingPayment) { + await deps.walletAddressService.setOn(outgoingPayment) + await deps.quoteService.setOn(outgoingPayment) + await deps.assetService.setOn(outgoingPayment.quote) return addSentAmount(deps, outgoingPayment) } } @@ -215,10 +224,24 @@ async function createOutgoingPayment( deps: ServiceDependencies, options: CreateOutgoingPaymentOptions ): Promise { + const stopTimerOP = deps.telemetry?.startTimer( + 'outgoing_payment_service_create_time_ms', + { + description: 'Time to create an outgoing payment' + } + ) + const { walletAddressId } = options let quoteId: string if (isCreateFromIncomingPayment(options)) { + const stopTimerQuote = deps.telemetry?.startTimer( + 'outgoing_payment_service_create_quote_time_ms', + { + description: 'Time to create a quote in outgoing payment' + } + ) + const { debitAmount, incomingPayment } = options const quoteOrError = await deps.quoteService.create({ receiver: incomingPayment, @@ -227,6 +250,8 @@ async function createOutgoingPayment( walletAddressId }) + stopTimerQuote && stopTimerQuote() + if (isQuoteError(quoteOrError)) { return quoteErrorToOutgoingPaymentError[quoteOrError] } @@ -238,32 +263,58 @@ async function createOutgoingPayment( const grantId = options.grant?.id try { return await OutgoingPayment.transaction(deps.knex, async (trx) => { + const stopTimerWA = deps.telemetry?.startTimer( + 'outgoing_payment_service_getwalletaddress_time_ms', + { + description: 'Time to get wallet address in outgoing payment' + } + ) + const walletAddress = await deps.walletAddressService.get(walletAddressId) + if (!walletAddress) { throw OutgoingPaymentError.UnknownWalletAddress } + stopTimerWA && stopTimerWA() + if (!walletAddress.isActive) { throw OutgoingPaymentError.InactiveWalletAddress } if (grantId) { + const stopTimerGrant = deps.telemetry?.startTimer( + 'outgoing_payment_service_insertgrant_time_ms', + { + description: 'Time to insert grant in outgoing payment' + } + ) await OutgoingPaymentGrant.query(trx) .insert({ id: grantId }) .onConflict('id') .ignore() + stopTimerGrant && stopTimerGrant() } - const payment = await OutgoingPayment.query(trx) - .insertAndFetch({ - id: quoteId, - walletAddressId: walletAddressId, - client: options.client, - metadata: options.metadata, - state: OutgoingPaymentState.Funding, - grantId - }) - .withGraphFetched('[quote.asset, walletAddress]') + const stopTimerInsertPayment = deps.telemetry?.startTimer( + 'outgoing_payment_service_insertpayment_time_ms', + { + description: 'Time to insert payment in outgoing payment' + } + ) + const payment = await OutgoingPayment.query(trx).insertAndFetch({ + id: quoteId, + walletAddressId: walletAddressId, + client: options.client, + metadata: options.metadata, + state: OutgoingPaymentState.Funding, + grantId + }) + await deps.quoteService.setOn(payment) + await deps.assetService.setOn(payment.quote) + await deps.walletAddressService.setOn(payment) + + stopTimerInsertPayment && stopTimerInsertPayment() if ( payment.walletAddressId !== payment.quote.walletAddressId || @@ -273,6 +324,13 @@ async function createOutgoingPayment( } if (options.grant) { + const stopTimerValidateGrant = deps.telemetry?.startTimer( + 'outgoing_payment_service_validate_grant_time_ms', + { + description: 'Time to validate a grant' + } + ) + const isValid = await validateGrantAndAddSpentAmountsToPayment( deps, payment, @@ -281,27 +339,78 @@ async function createOutgoingPayment( options.callback, options.grantLockTimeoutMs ) + + stopTimerValidateGrant && stopTimerValidateGrant() if (!isValid) { throw OutgoingPaymentError.InsufficientGrant } } + + const stopTimerReceiver = deps.telemetry?.startTimer( + 'outgoing_payment_service_getreceiver_time_ms', + { + description: 'Time to retrieve receiver in outgoing payment' + } + ) + const receiver = await deps.receiverService.get(payment.receiver) + + stopTimerReceiver && stopTimerReceiver() + if (!receiver) { throw OutgoingPaymentError.InvalidQuote } + + const stopTimerPeer = deps.telemetry?.startTimer( + 'outgoing_payment_service_getpeer_time_ms', + { + description: 'Time to retrieve peer in outgoing payment' + } + ) const peer = await deps.peerService.getByDestinationAddress( receiver.ilpAddress ) + stopTimerPeer && stopTimerPeer() + + const stopTimerPeerUpdate = deps.telemetry?.startTimer( + 'outgoing_payment_service_patchpeer_time_ms', + { + description: 'Time to patch peer in outgoing payment' + } + ) if (peer) await payment.$query(trx).patch({ peerId: peer.id }) + stopTimerPeerUpdate && stopTimerPeerUpdate() + const stopTimerWebhook = deps.telemetry?.startTimer( + 'outgoing_payment_service_webhook_event_time_ms', + { + description: 'Time to add outgoing payment webhook event' + } + ) await sendWebhookEvent( deps, payment, OutgoingPaymentEventType.PaymentCreated, trx ) + stopTimerWebhook && stopTimerWebhook() + + const stopTimerAddAmount = deps.telemetry?.startTimer( + 'outgoing_payment_service_add_sent_time_ms', + { + description: 'Time to add sent amount to outgoing payment' + } + ) - return await addSentAmount(deps, payment, BigInt(0)) + const paymentWithSentAmount = await addSentAmount( + deps, + payment, + BigInt(0) + ) + + stopTimerAddAmount && stopTimerAddAmount() + + return paymentWithSentAmount }) } catch (err) { if (err instanceof UniqueViolationError) { @@ -325,6 +434,8 @@ async function createOutgoingPayment( ) } throw err + } finally { + stopTimerOP && stopTimerOP() } } @@ -427,10 +538,11 @@ async function validateGrantAndAddSpentAmountsToPayment( .andWhereNot({ id: payment.id }) - .withGraphFetched('[quote.asset]') + if (grantPayments.length === 0) return true - if (grantPayments.length === 0) { - return true + for (const payment of grantPayments) { + await deps.quoteService.setOn(payment) + await deps.assetService.setOn(payment.quote) } const amounts = { @@ -500,16 +612,34 @@ async function fundPayment( deps: ServiceDependencies, { id, amount, transferId }: FundOutgoingPaymentOptions ): Promise { - return deps.knex.transaction(async (trx) => { - const payment = await OutgoingPayment.query(trx) - .findById(id) - .forUpdate() - .withGraphFetched('[quote.asset]') - if (!payment) return FundingError.UnknownPayment + const stopTimer = deps.telemetry?.startTimer('fundPayment', { + callName: 'fundPayment' + }) + const outgoingPaymentOrError = deps.knex.transaction(async (trx) => { + const cache = (await deps.cacheDataStore.get(id)) as OutgoingPayment + let payment + if (cache) { + payment = cache + } else { + payment = await OutgoingPayment.query(trx).findById(id) + await deps.cacheDataStore.set(id, payment) + } + if (!payment) { + stopTimer && stopTimer() + return FundingError.UnknownPayment + } + + await deps.quoteService.setOn(payment) + await deps.assetService.setOn(payment.quote) + if (payment.state !== OutgoingPaymentState.Funding) { + stopTimer && stopTimer() return FundingError.WrongState } - if (amount !== payment.debitAmount.value) return FundingError.InvalidAmount + if (amount !== payment.debitAmount.value) { + stopTimer && stopTimer() + return FundingError.InvalidAmount + } // Create the outgoing payment liquidity account before trying to transfer funds to it. try { @@ -525,6 +655,7 @@ async function fundPayment( if (err instanceof AccountAlreadyExistsError) { // Do nothing. } else { + stopTimer && stopTimer() throw err } } @@ -535,23 +666,34 @@ async function fundPayment( amount }) if (error) { + stopTimer && stopTimer() return error } await payment.$query(trx).patch({ state: OutgoingPaymentState.Sending }) - return await addSentAmount(deps, payment) + const paymentWithSentAmount = await addSentAmount(deps, payment) + stopTimer && stopTimer() + return paymentWithSentAmount }) + stopTimer && stopTimer() + return outgoingPaymentOrError } -async function getWalletAddressPage( +async function getOutgoingPaymentPage( deps: ServiceDependencies, options: ListOptions ): Promise { - const page = await OutgoingPayment.query(deps.knex) - .list(options) - .withGraphFetched('[quote.asset, walletAddress]') + const page = await OutgoingPayment.query(deps.knex).list(options) const amounts = await deps.accountingService.getAccountsTotalSent( page.map((payment: OutgoingPayment) => payment.id) ) + + for (const payment of page) { + await deps.walletAddressService.setOn(payment) + await deps.assetService.setOn(payment.walletAddress) + await deps.quoteService.setOn(payment) + await deps.assetService.setOn(payment.quote) + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types return page.map((payment: OutgoingPayment, i: number) => { payment.sentAmount = { diff --git a/packages/backend/src/open_payments/payment/outgoing/worker.ts b/packages/backend/src/open_payments/payment/outgoing/worker.ts index 2b501f6150..8c48ad0093 100644 --- a/packages/backend/src/open_payments/payment/outgoing/worker.ts +++ b/packages/backend/src/open_payments/payment/outgoing/worker.ts @@ -21,6 +21,9 @@ export async function processPendingPayment( return tracer.startActiveSpan( 'outgoingPaymentLifecycle', async (span: Span) => { + const stopTimer = deps_.telemetry?.startTimer('processPendingPayment', { + callName: 'processPendingPayment' + }) const paymentId = await deps_.knex.transaction(async (trx) => { const payment = await getPendingPayment(trx, deps_) if (!payment) return @@ -38,7 +41,7 @@ export async function processPendingPayment( ) return payment.id }) - + stopTimer && stopTimer() span.end() return paymentId } @@ -50,10 +53,9 @@ async function getPendingPayment( trx: Knex.Transaction, deps: ServiceDependencies ): Promise { - deps.telemetry && - deps.telemetry.startTimer('getPendingPayment', { - callName: 'getPendingPayment' - }) + const stopTimer = deps.telemetry?.startTimer('getPendingPayment', { + callName: 'getPendingPayment' + }) const now = new Date(Date.now()).toISOString() const payments = await OutgoingPayment.query(trx) .limit(1) @@ -71,8 +73,12 @@ async function getPendingPayment( [RETRY_BACKOFF_SECONDS, now] ) }) - .withGraphFetched('[walletAddress, quote.asset]') - deps.telemetry && deps.telemetry.stopTimer('getPendingPayment') + if (payments[0]) { + await deps.quoteService.setOn(payments[0]) + await deps.walletAddressService.setOn(payments[0]) + } + + stopTimer && stopTimer() return payments[0] } @@ -85,15 +91,16 @@ async function handlePaymentLifecycle( return } + const stopTimer = deps.telemetry?.startTimer('handleSending', { + callName: 'handleSending' + }) + try { - deps.telemetry && - deps.telemetry.startTimer('handleSending', { - callName: 'handleSending' - }) await lifecycle.handleSending(deps, payment) - deps.telemetry && deps.telemetry.stopTimer('handleSending') + stopTimer && stopTimer() } catch (error) { await onLifecycleError(deps, payment, error as Error | PaymentError) + stopTimer && stopTimer() } } diff --git a/packages/backend/src/open_payments/quote/service.test.ts b/packages/backend/src/open_payments/quote/service.test.ts index 4bddd700f5..0e5108c3fa 100644 --- a/packages/backend/src/open_payments/quote/service.test.ts +++ b/packages/backend/src/open_payments/quote/service.test.ts @@ -70,7 +70,10 @@ describe('QuoteService', (): void => { } beforeAll(async (): Promise => { - deps = initIocContainer(Config) + deps = initIocContainer({ + ...Config, + localCacheDuration: 0 + }) appContainer = await createTestApp(deps) knex = appContainer.knex diff --git a/packages/backend/src/open_payments/quote/service.ts b/packages/backend/src/open_payments/quote/service.ts index 3332f023f3..414b695b0d 100644 --- a/packages/backend/src/open_payments/quote/service.ts +++ b/packages/backend/src/open_payments/quote/service.ts @@ -21,11 +21,17 @@ import { PaymentMethodHandlerErrorCode } from '../../payment-method/handler/errors' import { TelemetryService } from '../../telemetry/service' +import { AssetService } from '../../asset/service' +import { OutgoingPayment } from '../payment/outgoing/model' +import { CacheDataStore } from '../../cache' const MAX_INT64 = BigInt('9223372036854775807') +export type ToSetOn = OutgoingPayment | undefined + export interface QuoteService extends WalletAddressSubresourceService { create(options: CreateQuoteOptions): Promise + setOn(obj: ToSetOn): Promise } export interface ServiceDependencies extends BaseService { @@ -35,6 +41,8 @@ export interface ServiceDependencies extends BaseService { walletAddressService: WalletAddressService feeService: FeeService paymentMethodHandlerService: PaymentMethodHandlerService + assetService: AssetService + cacheDataStore: CacheDataStore telemetry?: TelemetryService } @@ -48,7 +56,8 @@ export async function createQuoteService( return { get: (options) => getQuote(deps, options), create: (options: CreateQuoteOptions) => createQuote(deps, options), - getWalletAddressPage: (options) => getWalletAddressPage(deps, options) + getWalletAddressPage: (options) => getQuotePage(deps, options), + setOn: (toSetOn) => setQuoteOn(deps, toSetOn) } } @@ -86,23 +95,22 @@ async function createQuote( deps: ServiceDependencies, options: CreateQuoteOptions ): Promise { - deps.telemetry && - deps.telemetry.startTimer('quote_service_create_time_ms', { - description: 'Time to create a quote' - }) + const stopTimer = deps.telemetry?.startTimer('quote_service_create_time_ms', { + description: 'Time to create a quote' + }) if (options.debitAmount && options.receiveAmount) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return QuoteError.InvalidAmount } const walletAddress = await deps.walletAddressService.get( options.walletAddressId ) if (!walletAddress) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return QuoteError.UnknownWalletAddress } if (!walletAddress.isActive) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return QuoteError.InactiveWalletAddress } if (options.debitAmount) { @@ -111,57 +119,56 @@ async function createQuote( options.debitAmount.assetCode !== walletAddress.asset.code || options.debitAmount.assetScale !== walletAddress.asset.scale ) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return QuoteError.InvalidAmount } } if (options.receiveAmount) { if (options.receiveAmount.value <= BigInt(0)) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return QuoteError.InvalidAmount } } try { - deps.telemetry && - deps.telemetry.startTimer( - 'quote_service_create_resolve_receiver_time_ms', - { - description: 'Time to resolve receiver' - } - ) + const stopTimerReceiver = deps.telemetry?.startTimer( + 'quote_service_create_resolve_receiver_time_ms', + { + description: 'Time to resolve receiver' + } + ) const receiver = await resolveReceiver(deps, options) + stopTimerReceiver && stopTimerReceiver() - deps.telemetry && - deps.telemetry.stopTimer('quote_service_create_resolve_receiver_time_ms') - - deps.telemetry && - deps.telemetry.startTimer('quote_service_create_get_quote_time_ms', { + const stopTimerQuote = deps.telemetry?.startTimer( + 'quote_service_create_get_quote_time_ms', + { description: 'Time to getQuote' - }) + } + ) const quote = await deps.paymentMethodHandlerService.getQuote('ILP', { walletAddress, receiver, receiveAmount: options.receiveAmount, debitAmount: options.debitAmount }) - deps.telemetry && - deps.telemetry.stopTimer('quote_service_create_get_quote_time_ms') + stopTimerQuote && stopTimerQuote() const maxPacketAmount = quote.additionalFields.maxPacketAmount as bigint - deps.telemetry && - deps.telemetry.startTimer('quote_service_create_get_latest_fee_time_ms', { + const stopTimerFee = deps.telemetry?.startTimer( + 'quote_service_create_get_latest_fee_time_ms', + { description: 'Time to getLatestFee' - }) + } + ) const sendingFee = await deps.feeService.getLatestFee( walletAddress.assetId, FeeType.Sending ) - deps.telemetry && - deps.telemetry.stopTimer('quote_service_create_get_latest_fee_time_ms') + stopTimerFee && stopTimerFee() - const q = await Quote.transaction(deps.knex, async (trx) => { + return await Quote.transaction(deps.knex, async (trx) => { const createdQuote = await Quote.query(trx) .insertAndFetch({ walletAddressId: options.walletAddressId, @@ -183,7 +190,7 @@ async function createQuote( }) .withGraphFetched('[asset, fee, walletAddress]') - return await finalizeQuote( + const quoteFin = await finalizeQuote( { ...deps, knex: trx @@ -192,14 +199,14 @@ async function createQuote( createdQuote, receiver ) - }) - - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + await deps.cacheDataStore.set(quoteFin.id, quoteFin) + stopTimer && stopTimer() - return q + return quoteFin + }) } catch (err) { if (isQuoteError(err)) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return err } @@ -207,12 +214,12 @@ async function createQuote( err instanceof PaymentMethodHandlerError && err.code === PaymentMethodHandlerErrorCode.QuoteNonPositiveReceiveAmount ) { - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() return QuoteError.NonPositiveReceiveAmount } deps.logger.error({ err }, 'error creating a quote') - deps.telemetry && deps.telemetry.stopTimer('quote_service_create_time_ms') + stopTimer && stopTimer() throw err } } @@ -318,7 +325,6 @@ function calculateFixedDeliveryQuoteAmounts( quote: Quote ): CalculateQuoteAmountsWithFeesResult { const fees = quote.fee?.calculate(quote.debitAmount.value) ?? BigInt(0) - const debitAmountValue = BigInt(quote.debitAmount.value) + fees if (debitAmountValue <= BigInt(0)) { @@ -397,11 +403,12 @@ async function finalizeQuote( } await quote.$query(deps.knex).patch(patchOptions) + await deps.cacheDataStore.delete(quote.id) return quote } -async function getWalletAddressPage( +async function getQuotePage( deps: ServiceDependencies, options: ListOptions ): Promise { @@ -409,3 +416,12 @@ async function getWalletAddressPage( .list(options) .withGraphFetched('[asset, fee, walletAddress]') } + +async function setQuoteOn( + deps: ServiceDependencies, + obj: ToSetOn +): Promise { + if (!obj) return + const quote = await getQuote(deps, { id: obj.id }) + if (quote) obj.quote = quote +} diff --git a/packages/backend/src/open_payments/receiver/service.ts b/packages/backend/src/open_payments/receiver/service.ts index 3fba5e6709..7929b8b1ea 100644 --- a/packages/backend/src/open_payments/receiver/service.ts +++ b/packages/backend/src/open_payments/receiver/service.ts @@ -138,20 +138,21 @@ async function getReceiver( deps: ServiceDependencies, url: string ): Promise { - deps.telemetry && - deps.telemetry.startTimer('getReceiver', { callName: 'getReceiver' }) + const stopTimer = deps.telemetry?.startTimer('getReceiver', { + callName: 'getReceiver' + }) try { const localIncomingPayment = await getLocalIncomingPayment(deps, url) if (localIncomingPayment) { const receiver = new Receiver(localIncomingPayment, true) - deps.telemetry && deps.telemetry.stopTimer('getReceiver') + stopTimer && stopTimer() return receiver } const remoteIncomingPayment = await getRemoteIncomingPayment(deps, url) if (remoteIncomingPayment) { const receiver = new Receiver(remoteIncomingPayment, false) - deps.telemetry && deps.telemetry.stopTimer('getReceiver') + stopTimer && stopTimer() return receiver } } catch (err) { @@ -159,7 +160,7 @@ async function getReceiver( { errorMessage: err instanceof Error && err.message }, 'Could not get incoming payment' ) - deps.telemetry && deps.telemetry.stopTimer('getReceiver') + stopTimer && stopTimer() } } diff --git a/packages/backend/src/open_payments/wallet_address/model.test.ts b/packages/backend/src/open_payments/wallet_address/model.test.ts index 6aaeb79184..01766d265b 100644 --- a/packages/backend/src/open_payments/wallet_address/model.test.ts +++ b/packages/backend/src/open_payments/wallet_address/model.test.ts @@ -138,7 +138,7 @@ const baseGetTests = ({ }) test(`${ match ? '' : 'cannot ' - }list model`, async (): Promise => { + }list model (get list)`, async (): Promise => { if (testList && model.walletAddressId) { await testList( { diff --git a/packages/backend/src/open_payments/wallet_address/model.ts b/packages/backend/src/open_payments/wallet_address/model.ts index 81dd603a1d..54b47bf724 100644 --- a/packages/backend/src/open_payments/wallet_address/model.ts +++ b/packages/backend/src/open_payments/wallet_address/model.ts @@ -67,10 +67,11 @@ export class WalletAddress public async onCredit({ totalReceived, - withdrawalThrottleDelay + withdrawalThrottleDelay, + fetchAssetService }: OnCreditOptions): Promise { if (this.asset.withdrawalThreshold !== null) { - const walletAddress = await WalletAddress.query() + let walletAddressQuery = WalletAddress.query() .patchAndFetchById(this.id, { processAt: new Date() }) @@ -78,10 +79,13 @@ export class WalletAddress 'totalEventsAmount', totalReceived - this.asset.withdrawalThreshold ]) - .withGraphFetched('asset') - if (walletAddress) { - return walletAddress - } + if (!fetchAssetService) + walletAddressQuery = walletAddressQuery.withGraphFetched('asset') + + const walletAddress = await walletAddressQuery + if (fetchAssetService) + await (await fetchAssetService()).setOn(walletAddress) + if (walletAddress) return walletAddress } if (withdrawalThrottleDelay !== undefined && !this.processAt) { await this.$query().patch({ diff --git a/packages/backend/src/open_payments/wallet_address/service.test.ts b/packages/backend/src/open_payments/wallet_address/service.test.ts index 40ce06f726..78dd1b93c3 100644 --- a/packages/backend/src/open_payments/wallet_address/service.test.ts +++ b/packages/backend/src/open_payments/wallet_address/service.test.ts @@ -35,7 +35,10 @@ describe('Open Payments Wallet Address Service', (): void => { let knex: Knex beforeAll(async (): Promise => { - deps = initIocContainer(Config) + deps = initIocContainer({ + ...Config, + localCacheDuration: 0 + }) config = await deps.use('config') appContainer = await createTestApp(deps) knex = appContainer.knex @@ -581,7 +584,8 @@ describe('Open Payments Wallet Address Service', (): void => { await expect( walletAddress.onCredit({ totalReceived: totalEventsAmount + BigInt(1), - withdrawalThrottleDelay + withdrawalThrottleDelay, + fetchAssetService: undefined }) ).resolves.toMatchObject({ processAt: startingProcessAt || delayProcessAt diff --git a/packages/backend/src/open_payments/wallet_address/service.ts b/packages/backend/src/open_payments/wallet_address/service.ts index 3a62c5e865..d3dda3b649 100644 --- a/packages/backend/src/open_payments/wallet_address/service.ts +++ b/packages/backend/src/open_payments/wallet_address/service.ts @@ -25,6 +25,12 @@ import { Pagination, SortOrder } from '../../shared/baseModel' import { WebhookService } from '../../webhook/service' import { poll } from '../../shared/utils' import { WalletAddressAdditionalProperty } from './additional_property/model' +import { AssetService } from '../../asset/service' +import { Quote } from '../quote/model' +import { CacheDataStore } from '../../cache' +import { OutgoingPayment } from '../payment/outgoing/model' + +export type ToSetOn = Quote | IncomingPayment | OutgoingPayment | undefined interface Options { publicName?: string @@ -70,6 +76,7 @@ export interface WalletAddressService { ): Promise processNext(): Promise triggerEvents(limit: number): Promise + setOn(obj: ToSetOn): Promise } interface ServiceDependencies extends BaseService { @@ -77,6 +84,8 @@ interface ServiceDependencies extends BaseService { knex: TransactionOrKnex accountingService: AccountingService webhookService: WebhookService + assetService: AssetService + cacheDataStore: CacheDataStore } export async function createWalletAddressService({ @@ -84,7 +93,9 @@ export async function createWalletAddressService({ config, knex, accountingService, - webhookService + webhookService, + assetService, + cacheDataStore }: ServiceDependencies): Promise { const log = logger.child({ service: 'WalletAddressService' @@ -94,7 +105,9 @@ export async function createWalletAddressService({ logger: log, knex, accountingService, - webhookService + webhookService, + assetService, + cacheDataStore } return { create: (options) => createWalletAddress(deps, options), @@ -111,7 +124,8 @@ export async function createWalletAddressService({ getPage: (pagination?, sortOrder?) => getWalletAddressPage(deps, pagination, sortOrder), processNext: () => processNextWalletAddress(deps), - triggerEvents: (limit) => triggerWalletAddressEvents(deps, limit) + triggerEvents: (limit) => triggerWalletAddressEvents(deps, limit), + setOn: (toSetOn) => setWalletAddressOn(deps, toSetOn) } } @@ -164,14 +178,17 @@ async function createWalletAddress( ? cleanAdditionalProperties(options.additionalProperties) : undefined - return await WalletAddress.query(deps.knex) - .insertGraphAndFetch({ - url: options.url, - publicName: options.publicName, - assetId: options.assetId, - additionalProperties: additionalProperties - }) - .withGraphFetched('asset') + const walletAddress = await WalletAddress.query( + deps.knex + ).insertGraphAndFetch({ + url: options.url, + publicName: options.publicName, + assetId: options.assetId, + additionalProperties: additionalProperties + }) + await deps.assetService.setOn(walletAddress) + await deps.cacheDataStore.set(walletAddress.id, walletAddress) + return walletAddress } catch (err) { if (err instanceof ForeignKeyViolationError) { if (err.constraint === 'walletaddresses_assetid_foreign') { @@ -203,8 +220,8 @@ async function updateWalletAddress( const updatedWalletAddress = await walletAddress .$query(trx) .patchAndFetch(update) - .withGraphFetched('asset') .throwIfNotFound() + await deps.assetService.setOn(updatedWalletAddress) // Override all existing additional properties if new ones are provided if (additionalProperties) { @@ -225,6 +242,7 @@ async function updateWalletAddress( } await trx.commit() + await deps.cacheDataStore.set(updatedWalletAddress.id, updatedWalletAddress) return updatedWalletAddress } catch (err) { await trx.rollback() @@ -239,9 +257,15 @@ async function getWalletAddress( deps: ServiceDependencies, id: string ): Promise { - return await WalletAddress.query(deps.knex) - .findById(id) - .withGraphFetched('asset') + const walletAdd = await deps.cacheDataStore.get(id) + if (walletAdd) return walletAdd as WalletAddress + + const walletAddress = await WalletAddress.query(deps.knex).findById(id) + if (walletAddress) { + await deps.assetService.setOn(walletAddress) + await deps.cacheDataStore.set(id, walletAddress) + } + return walletAddress } async function getWalletAdditionalProperties( @@ -295,9 +319,8 @@ async function getWalletAddressByUrl( deps: ServiceDependencies, url: string ): Promise { - const walletAddress = await WalletAddress.query(deps.knex) - .findOne({ url }) - .withGraphFetched('asset') + const walletAddress = await WalletAddress.query(deps.knex).findOne({ url }) + await deps.assetService.setOn(walletAddress) return walletAddress || undefined } @@ -306,7 +329,7 @@ async function getWalletAddressPage( pagination?: Pagination, sortOrder?: SortOrder ): Promise { - return await WalletAddress.query(deps.knex) + return WalletAddress.query(deps.knex) .getPage(pagination, sortOrder) .withGraphFetched('asset') } @@ -342,7 +365,10 @@ async function processNextWalletAddresses( // If a wallet address is locked, don't wait — just come back for it later. .skipLocked() .where('processAt', '<=', now) - .withGraphFetched('asset') + + for (const walletAddress of walletAddresses) { + await deps_.assetService.setOn(walletAddress) + } const deps = { ...deps_, @@ -425,8 +451,13 @@ async function deactivateOpenIncomingPaymentsByWalletAddress( .where('expiresAt', '>', expiresAt) } -export interface CreateSubresourceOptions { - walletAddressId: string +async function setWalletAddressOn( + deps: ServiceDependencies, + obj: ToSetOn +): Promise { + if (!obj) return + const walletAddress = await getWalletAddress(deps, obj.walletAddressId) + if (walletAddress) obj.walletAddress = walletAddress } export interface WalletAddressSubresourceService< diff --git a/packages/backend/src/payment-method/ilp/connector/core/middleware/balance.ts b/packages/backend/src/payment-method/ilp/connector/core/middleware/balance.ts index 48b6a2fa64..cea3146a59 100644 --- a/packages/backend/src/payment-method/ilp/connector/core/middleware/balance.ts +++ b/packages/backend/src/payment-method/ilp/connector/core/middleware/balance.ts @@ -20,10 +20,9 @@ export function createBalanceMiddleware(): ILPMiddleware { }: ILPContext, next: () => Promise ): Promise => { - services.telemetry && - services.telemetry.startTimer('balanceMiddleware', { - callName: 'balanceMiddleware' - }) + const stopTimer = services.telemetry?.startTimer('balanceMiddleware', { + callName: 'balanceMiddleware' + }) const { amount } = request.prepare const logger = services.logger.child( { module: 'balance-middleware' }, @@ -35,7 +34,7 @@ export function createBalanceMiddleware(): ILPMiddleware { // Ignore zero amount packets if (amount === '0') { await next() - services.telemetry && services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() return } @@ -56,7 +55,7 @@ export function createBalanceMiddleware(): ILPMiddleware { }, 'Could not get rates' ) - services.telemetry && services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() throw new CannotReceiveError( `Exchange rate error: ${destinationAmountOrError}` ) @@ -66,7 +65,7 @@ export function createBalanceMiddleware(): ILPMiddleware { if (state.unfulfillable) { await next() - services.telemetry && services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() return } @@ -92,17 +91,15 @@ export function createBalanceMiddleware(): ILPMiddleware { switch (trxOrError) { case TransferError.InsufficientBalance: case TransferError.InsufficientLiquidity: - services.telemetry && - services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() throw new InsufficientLiquidityError(trxOrError) default: - services.telemetry && - services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() // TODO: map transfer errors to ILP errors ctxThrow(500, destinationAmountOrError.toString()) } } else { - services.telemetry && services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() return trxOrError } } @@ -121,7 +118,7 @@ export function createBalanceMiddleware(): ILPMiddleware { } else { await trx.void() } - services.telemetry && services.telemetry.stopTimer('balanceMiddleware') + stopTimer && stopTimer() } } } diff --git a/packages/backend/src/payment-method/ilp/connector/core/middleware/error-handler.ts b/packages/backend/src/payment-method/ilp/connector/core/middleware/error-handler.ts index 026c74cce5..69b86a98b7 100644 --- a/packages/backend/src/payment-method/ilp/connector/core/middleware/error-handler.ts +++ b/packages/backend/src/payment-method/ilp/connector/core/middleware/error-handler.ts @@ -11,21 +11,18 @@ export function createIncomingErrorHandlerMiddleware( serverAddress: string ): ILPMiddleware { return async (ctx: ILPContext, next: () => Promise): Promise => { + let stopTimer try { await next() - ctx.services.telemetry && - ctx.services.telemetry.startTimer( - 'createIncomingErrorHandlerMiddleware', - { - callName: 'createIncomingErrorHandlerMiddleware' - } - ) + stopTimer = ctx.services.telemetry?.startTimer( + 'createIncomingErrorHandlerMiddleware', + { + callName: 'createIncomingErrorHandlerMiddleware' + } + ) if (!ctx.response.rawReply) { ctx.services.logger.error('handler did not return a valid value.') - ctx.services.telemetry && - ctx.services.telemetry.stopTimer( - 'createIncomingErrorHandlerMiddleware' - ) + stopTimer && stopTimer() throw new Error('handler did not return a value.') } } catch (e) { @@ -48,8 +45,7 @@ export function createIncomingErrorHandlerMiddleware( name: '' }) } - ctx.services.telemetry && - ctx.services.telemetry.stopTimer('createIncomingErrorHandlerMiddleware') + stopTimer && stopTimer() } } } diff --git a/packages/backend/src/payment-method/ilp/connector/core/middleware/stream-address.ts b/packages/backend/src/payment-method/ilp/connector/core/middleware/stream-address.ts index 360f9133e9..7167648625 100644 --- a/packages/backend/src/payment-method/ilp/connector/core/middleware/stream-address.ts +++ b/packages/backend/src/payment-method/ilp/connector/core/middleware/stream-address.ts @@ -5,14 +5,13 @@ export function createStreamAddressMiddleware(): ILPMiddleware { { request, services: { streamServer, telemetry }, state }: ILPContext, next: () => Promise ): Promise => { - telemetry && - telemetry.startTimer('createStreamAddressMiddleware', { - callName: 'createStreamAddressMiddleware' - }) + const stopTimer = telemetry?.startTimer('createStreamAddressMiddleware', { + callName: 'createStreamAddressMiddleware' + }) const { destination } = request.prepare // To preserve sender privacy, the accountId wasn't included in the original destination address. state.streamDestination = streamServer.decodePaymentTag(destination) - telemetry && telemetry.stopTimer('createStreamAddressMiddleware') + stopTimer && stopTimer() await next() } } diff --git a/packages/backend/src/payment-method/ilp/connector/index.ts b/packages/backend/src/payment-method/ilp/connector/index.ts index ae3c859b6e..4fc9db17db 100644 --- a/packages/backend/src/payment-method/ilp/connector/index.ts +++ b/packages/backend/src/payment-method/ilp/connector/index.ts @@ -139,25 +139,24 @@ function compose( // last called middleware let index = -1 - telemetry && - telemetry.startTimer('connector_middleware_stack', { - callName: 'connector_middleware_stack' - }) + const stopTimer = telemetry?.startTimer('connector_middleware_stack', { + callName: 'connector_middleware_stack' + }) async function dispatch(i: number): Promise { if (i <= index) return Promise.reject(new Error('next() called multiple times')) index = i - let m = middlewares[i] + const m = middlewares[i] if (i === middlewares.length) m.fn = next if (!m.fn) return Promise.resolve() try { - telemetry && - telemetry.startTimer('connector_middleware', { - callName: m.name - }) + const stopTimerMiddleware = telemetry?.startTimer( + 'connector_middleware', + { callName: m.name } + ) const p = Promise.resolve(m.fn(ctx, dispatch.bind(null, i + 1))) - telemetry && telemetry.stopTimer('connector_middleware') + stopTimerMiddleware && stopTimerMiddleware() return p } catch (err) { return Promise.reject(err) @@ -165,7 +164,7 @@ function compose( } return dispatch(0).finally(() => { - telemetry && telemetry.stopTimer('connector_middleware_stack') + stopTimer && stopTimer() }) } } diff --git a/packages/backend/src/payment-method/ilp/peer/service.ts b/packages/backend/src/payment-method/ilp/peer/service.ts index fc39c49a60..e496244fd5 100644 --- a/packages/backend/src/payment-method/ilp/peer/service.ts +++ b/packages/backend/src/payment-method/ilp/peer/service.ts @@ -114,7 +114,9 @@ async function getPeer( deps: ServiceDependencies, id: string ): Promise { - return Peer.query(deps.knex).findById(id).withGraphFetched('asset') + const peer = await Peer.query(deps.knex).findById(id) + await deps.assetService.setOn(peer) + return peer } async function createPeer( @@ -131,16 +133,15 @@ async function createPeer( try { return await Peer.transaction(deps.knex, async (trx) => { - const peer = await Peer.query(trx) - .insertAndFetch({ - assetId: options.assetId, - http: options.http, - maxPacketAmount: options.maxPacketAmount, - staticIlpAddress: options.staticIlpAddress, - name: options.name, - liquidityThreshold: options.liquidityThreshold - }) - .withGraphFetched('asset') + const peer = await Peer.query(trx).insertAndFetch({ + assetId: options.assetId, + http: options.http, + maxPacketAmount: options.maxPacketAmount, + staticIlpAddress: options.staticIlpAddress, + name: options.name, + liquidityThreshold: options.liquidityThreshold + }) + await deps.assetService.setOn(peer) if (options.http?.incoming) { const err = await addIncomingHttpTokens({ @@ -232,10 +233,12 @@ async function updatePeer( throw err } } - return await Peer.query(trx) + + const peer = await Peer.query(trx) .patchAndFetchById(options.id, options) - .withGraphFetched('asset') .throwIfNotFound() + await deps.assetService.setOn(peer) + return peer }) } catch (err) { if (err instanceof NotFoundError) { @@ -314,7 +317,6 @@ async function getPeerByDestinationAddress( // for `staticIlpAddress`s in the accounts table: // new RegExp('^' + staticIlpAddress + '($|\\.)')).test(destinationAddress) const peerQuery = Peer.query(deps.knex) - .withGraphFetched('asset') .where( raw('?', [destinationAddress]), 'like', @@ -344,6 +346,7 @@ async function getPeerByDestinationAddress( } const peer = await peerQuery.first() + await deps.assetService.setOn(peer) return peer || undefined } @@ -375,18 +378,18 @@ async function getPeersPage( pagination?: Pagination, sortOrder?: SortOrder ): Promise { - return await Peer.query(deps.knex) - .getPage(pagination, sortOrder) - .withGraphFetched('asset') + const peers = await Peer.query(deps.knex).getPage(pagination, sortOrder) + for (const peer of peers) { + await deps.assetService.setOn(peer) + } + return peers } async function deletePeer( deps: ServiceDependencies, id: string ): Promise { - return Peer.query(deps.knex) - .withGraphFetched('asset') - .deleteById(id) - .returning('*') - .first() + const peer = await Peer.query(deps.knex).deleteById(id).returning('*').first() + await deps.assetService.setOn(peer) + return peer } diff --git a/packages/backend/src/payment-method/ilp/service.ts b/packages/backend/src/payment-method/ilp/service.ts index ac1147ee67..ce5fddbc93 100644 --- a/packages/backend/src/payment-method/ilp/service.ts +++ b/packages/backend/src/payment-method/ilp/service.ts @@ -43,40 +43,46 @@ async function getQuote( deps: ServiceDependencies, options: StartQuoteOptions ): Promise { - deps.telemetry && - deps.telemetry.startTimer('ilp_get_quote_rate_time_ms', { + const stopTimerRates = deps.telemetry?.startTimer( + 'ilp_get_quote_rate_time_ms', + { description: 'Time to get rates' - }) + } + ) const rates = await deps.ratesService .rates(options.walletAddress.asset.code) .catch((_err: Error) => { + stopTimerRates && stopTimerRates() throw new PaymentMethodHandlerError('Received error during ILP quoting', { description: 'Could not get rates from service', retryable: false }) }) - deps.telemetry && deps.telemetry.stopTimer('ilp_get_quote_rate_time_ms') + stopTimerRates && stopTimerRates() - deps.telemetry && - deps.telemetry.startTimer('ilp_make_ilp_plugin_time_ms', { + const stopTimerPlugin = deps.telemetry?.startTimer( + 'ilp_make_ilp_plugin_time_ms', + { description: 'Time to make ilp plugin' - }) + } + ) const plugin = deps.makeIlpPlugin({ sourceAccount: options.walletAddress, unfulfillable: true }) - deps.telemetry && deps.telemetry.stopTimer('ilp_make_ilp_plugin_time_ms') + stopTimerPlugin && stopTimerPlugin() const destination = options.receiver.toResolvedPayment() try { - deps.telemetry && - deps.telemetry.startTimer('ilp_make_ilp_plugin_connect_time_ms', { + const stopTimerConnect = deps.telemetry?.startTimer( + 'ilp_make_ilp_plugin_connect_time_ms', + { description: 'Time to connect ilp plugin' - }) + } + ) await plugin.connect() - deps.telemetry && - deps.telemetry.stopTimer('ilp_make_ilp_plugin_connect_time_ms') + stopTimerConnect && stopTimerConnect() const quoteOptions: Pay.QuoteOptions = { plugin, destination, @@ -93,10 +99,12 @@ async function getQuote( } let ilpQuote: Pay.Quote | undefined - deps.telemetry && - deps.telemetry.startTimer('ilp_rate_probe_time_ms', { + const stopTimerProbe = deps.telemetry?.startTimer( + 'ilp_rate_probe_time_ms', + { description: 'Time to get an ILP quote (Pay.startQuote)' - }) + } + ) try { ilpQuote = await Pay.startQuote({ ...quoteOptions, @@ -107,12 +115,14 @@ async function getQuote( const errorMessage = 'Received error during ILP quoting' deps.logger.error({ err }, errorMessage) + stopTimerProbe && stopTimerProbe() + throw new PaymentMethodHandlerError(errorMessage, { description: Pay.isPaymentError(err) ? err : 'Unknown error', retryable: canRetryError(err as Error | Pay.PaymentError) }) } - deps.telemetry && deps.telemetry.stopTimer('ilp_rate_probe_time_ms') + stopTimerProbe && stopTimerProbe() // Pay.startQuote should return PaymentError.InvalidSourceAmount or // PaymentError.InvalidDestinationAmount for non-positive amounts. // Outgoing payments' sendAmount or receiveAmount should never be @@ -182,14 +192,13 @@ async function getQuote( } } } finally { + const stopTimerClose = deps.telemetry?.startTimer( + 'ilp_plugin_close_connect_time_ms', + { description: 'Time to close ilp plugin' } + ) try { - deps.telemetry && - deps.telemetry.startTimer('ilp_plugin_close_connect_time_ms', { - description: 'Time to close ilp plugin' - }) await Pay.closeConnection(plugin, destination) - deps.telemetry && - deps.telemetry.stopTimer('ilp_plugin_close_connect_time_ms') + stopTimerClose && stopTimerClose() } catch (error) { deps.logger.warn( { @@ -198,6 +207,7 @@ async function getQuote( }, 'close quote connection failed' ) + stopTimerClose && stopTimerClose() } try { diff --git a/packages/backend/src/telemetry/service.ts b/packages/backend/src/telemetry/service.ts index 51dd31e0e0..f7fa228f56 100644 --- a/packages/backend/src/telemetry/service.ts +++ b/packages/backend/src/telemetry/service.ts @@ -30,8 +30,7 @@ export interface TelemetryService { value: number, attributes?: Record ): void - startTimer(name: string, attributes?: Record): void - stopTimer(name: string): void + startTimer(name: string, attributes?: Record): () => void } interface TelemetryServiceDependencies extends BaseService { @@ -193,16 +192,14 @@ class TelemetryServiceImpl implements TelemetryService { }) } - public startTimer(name: string, attributes: Record = {}) { - this.timers.set(name, { start: Date.now(), attributes }) - } - public stopTimer(name: string) { - const timer = this.timers.get(name) - if (!timer) { - return + public startTimer( + name: string, + attributes: Record = {} + ): () => void { + const start = Date.now() + return () => { + this.recordHistogram(name, Date.now() - start, attributes) } - const end = Date.now() - this.recordHistogram(name, end - timer.start, timer.attributes) } private async convertAmount( diff --git a/packages/backend/src/tests/telemetry.ts b/packages/backend/src/tests/telemetry.ts index 2d22dc9e53..60fb54d178 100644 --- a/packages/backend/src/tests/telemetry.ts +++ b/packages/backend/src/tests/telemetry.ts @@ -39,8 +39,13 @@ export class MockTelemetryService implements TelemetryService { public getInstanceName(): string | undefined { return 'serviceName' } - public startTimer(name: string, attributes?: Record): void {} - public stopTimer(name: string, attributes?: Record): void {} + /* eslint-disable @typescript-eslint/no-unused-vars */ + public startTimer( + name: string, + attributes?: Record + ): () => void { + return () => undefined + } public async shutdown(): Promise {} public async convertAmount( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b90f661df..b8ee2545c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -745,21 +745,6 @@ importers: specifier: ^6.7.5 version: 6.7.5 - test/grpc-http-benchmark: - dependencies: - '@grpc/grpc-js': - specifier: ^1.11.1 - version: 1.11.1 - '@grpc/proto-loader': - specifier: ^0.7.13 - version: 0.7.13 - '@koa/router': - specifier: ^12.0.0 - version: 12.0.0 - koa: - specifier: ^2.15.3 - version: 2.15.3 - test/integration: devDependencies: '@apollo/client': @@ -4218,14 +4203,6 @@ packages: '@js-sdsl/ordered-map': 4.4.2 dev: false - /@grpc/grpc-js@1.11.1: - resolution: {integrity: sha512-gyt/WayZrVPH2w/UTLansS7F9Nwld472JxxaETamrM8HNlsa+jSLNyKAZmhxI2Me4c3mQHFiS1wWHDY1g1Kthw==} - engines: {node: '>=12.10.0'} - dependencies: - '@grpc/proto-loader': 0.7.13 - '@js-sdsl/ordered-map': 4.4.2 - dev: false - /@grpc/proto-loader@0.7.13: resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} engines: {node: '>=6'} diff --git a/test/performance/scripts/create-outgoing-payments.js b/test/performance/scripts/create-outgoing-payments.js index 67eb293f98..30cbe38562 100644 --- a/test/performance/scripts/create-outgoing-payments.js +++ b/test/performance/scripts/create-outgoing-payments.js @@ -2,7 +2,7 @@ import http from 'k6/http' import { fail } from 'k6' export const options = { // A number specifying the number of VUs to run concurrently. - vus: 9, + vus: 1, // A string specifying the total duration of the test run. duration: '120s' }