Commit ea2000b
authored
telemetry: surface program errors from finalized transactions (#4152)
Resolves: malbeclabs/infra#1703
## Summary of Changes
- The Go telemetry SDK executor no longer reports a transaction the
program rejected as a success. Finalization only means the cluster
agreed on the transaction: an instruction the program refused finalizes
too, carrying the rejection in `err`, which the executor never read. It
now returns a `*telemetry.ProgramError` holding the ledger's error and
the program's log output.
- The device telemetry submitter treats that rejection as permanent for
the tick — it logs the program's own explanation at `Error` and leaves
the tick's remaining attempts unspent, rather than spending them on an
instruction the ledger has already refused. New
`submitter_program_error` type on the existing errors counter.
- `ProgramError.Error()` leads with the program's explanation (`Program
log:` lines, minus the runtime's invoke/consumed boilerplate and the
instruction-name echo), so a caller that only prints the error still
gets the reason. This is the check
`smartcontract/sdk/go/serviceability/executor.go` already made;
telemetry was the outlier.
This is what left chi-dn-dzd4 silent for over an hour. Its
`metrics_publisher` had been set to a key the agent did not hold, and
the init half of the init→write path skips preflight — so
`UnauthorizedAgent` (0x3e9) only arrived on the finalized transaction,
which the SDK read as success. The agent looped init → write → `account
not found` every few seconds, and the only error it printed named the
missing account, not the authorization failure that caused it.
## Diff Breakdown
| Category | Files | Lines (+/-) | Net |
|--------------|-------|-------------|------|
| Tests | 2 | +178 / -0 | +178 |
| Core logic | 2 | +86 / -0 | +86 |
| Docs | 1 | +3 / -0 | +3 |
| Scaffolding | 1 | +4 / -0 | +4 |
| **Total** | 6 | +271 / -0 | +271 |
Two thirds tests: the fix itself is 86 lines across the SDK executor and
one branch in the submitter's retry loop.
<details>
<summary>Key files (click to expand)</summary>
- `smartcontract/sdk/go/telemetry/executor.go` — new `ProgramError` type
with the program-log filter; `waitForTransactionFinalized` returns it
when the finalized signature status or the transaction meta carries an
error, fetching the logs best effort so a node that cannot return the
transaction costs the logs rather than replacing the rejection with an
RPC error
- `controlplane/telemetry/internal/telemetry/submitter.go` — `errors.As`
branch in `Tick`'s retry loop: count, log at `Error`, stop the tick's
attempts; samples requeue as with any other failure
- `controlplane/telemetry/internal/metrics/metrics.go` —
`ErrorTypeSubmitterProgramError`, kept distinct from the write/init
failure types because those also cover transient RPC trouble
</details>
## Testing Verification
- `TestSDK_Telemetry_Executor_FinalizedWithProgramError` reproduces the
chi-dn-dzd4 transaction — finalized, `Custom: 1001`, with the "not
authorized for origin device" program log — across three shapes: the
rejection on the signature status, on the transaction meta only (a node
that returns a clean status), and with the logs unfetchable. All three
must return an error rather than a signature; the error message carries
the custom code in every case and the program's explanation whenever the
logs were available, with the boilerplate stripped.
- `does_not_retry_a_submission_the_program_rejected` drives a full
submitter tick with `MaxAttempts: 5` and asserts exactly one write and
one init reach the program, `submitter_program_error` increments once,
`submitter_retries_exhausted` does not, the reason reaches the log, and
the samples are requeued for the next tick.
- The two pre-existing finalization edge cases (`Meta == nil`,
`GetTransaction` returning nil) keep their original error messages — a
clean signature status still falls through to them unchanged.
- Full suites pass for `smartcontract/sdk/go/telemetry`,
`controlplane/telemetry/...`, and
`controlplane/internet-latency-collector/...`. One unrelated
pre-existing failure in `controlplane/telemetry/internal/netns`
(`TestRunInNamespace_EmptyNameErrors` needs namespace privileges)
reproduces identically on a clean `main` checkout.
### Not in scope
-
`controlplane/internet-latency-collector/internal/exporter/submitter.go`
has the same init→write shape and picks up the SDK's real error for
free, but keeps retrying a rejection through its attempts. Left for a
follow-up rather than widened here.
- A write that passes preflight and then finalizes against a full
account now surfaces as a generic `ProgramError` instead of the
`ErrSamplesAccountFull` sentinel, so it requeues and takes the drop path
on the next tick instead of immediately. Previously that race reported
success and lost the samples with no signal at all, so this is strictly
better; mapping `meta.err` custom codes back onto the sentinels would
need a parser in `client.go` and is a separate change.1 parent 1662e79 commit ea2000b
12 files changed
Lines changed: 673 additions & 26 deletions
File tree
- controlplane
- internet-latency-collector/internal/exporter
- telemetry/internal
- metrics
- telemetry
- e2e
- smartcontract/sdk/go/telemetry
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
13 | 18 | | |
| 19 | + | |
14 | 20 | | |
15 | 21 | | |
16 | 22 | | |
| |||
Lines changed: 24 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
107 | | - | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
108 | 112 | | |
109 | 113 | | |
110 | 114 | | |
111 | 115 | | |
112 | | - | |
| 116 | + | |
113 | 117 | | |
114 | 118 | | |
115 | 119 | | |
| |||
140 | 144 | | |
141 | 145 | | |
142 | 146 | | |
143 | | - | |
| 147 | + | |
144 | 148 | | |
145 | 149 | | |
146 | 150 | | |
| |||
150 | 154 | | |
151 | 155 | | |
152 | 156 | | |
153 | | - | |
| 157 | + | |
154 | 158 | | |
155 | 159 | | |
156 | 160 | | |
157 | 161 | | |
158 | | - | |
| 162 | + | |
159 | 163 | | |
160 | 164 | | |
161 | | - | |
| 165 | + | |
162 | 166 | | |
163 | | - | |
| 167 | + | |
164 | 168 | | |
165 | 169 | | |
166 | | - | |
| 170 | + | |
167 | 171 | | |
168 | 172 | | |
169 | | - | |
| 173 | + | |
170 | 174 | | |
171 | | - | |
| 175 | + | |
172 | 176 | | |
173 | 177 | | |
174 | 178 | | |
175 | 179 | | |
176 | | - | |
| 180 | + | |
177 | 181 | | |
178 | 182 | | |
179 | | - | |
| 183 | + | |
180 | 184 | | |
181 | 185 | | |
182 | 186 | | |
| |||
232 | 236 | | |
233 | 237 | | |
234 | 238 | | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
235 | 244 | | |
236 | 245 | | |
237 | 246 | | |
238 | 247 | | |
239 | 248 | | |
240 | 249 | | |
241 | | - | |
| 250 | + | |
242 | 251 | | |
| 252 | + | |
243 | 253 | | |
244 | 254 | | |
245 | 255 | | |
| |||
272 | 282 | | |
273 | 283 | | |
274 | 284 | | |
275 | | - | |
| 285 | + | |
276 | 286 | | |
277 | 287 | | |
278 | 288 | | |
| |||
Lines changed: 52 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
741 | 741 | | |
742 | 742 | | |
743 | 743 | | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
| 752 | + | |
| 753 | + | |
| 754 | + | |
| 755 | + | |
| 756 | + | |
| 757 | + | |
| 758 | + | |
| 759 | + | |
| 760 | + | |
| 761 | + | |
| 762 | + | |
| 763 | + | |
| 764 | + | |
| 765 | + | |
| 766 | + | |
| 767 | + | |
| 768 | + | |
| 769 | + | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
| 782 | + | |
| 783 | + | |
| 784 | + | |
| 785 | + | |
| 786 | + | |
| 787 | + | |
| 788 | + | |
| 789 | + | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
744 | 796 | | |
745 | 797 | | |
746 | 798 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
46 | 52 | | |
47 | 53 | | |
48 | 54 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
139 | | - | |
| 139 | + | |
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| |||
146 | 146 | | |
147 | 147 | | |
148 | 148 | | |
149 | | - | |
150 | | - | |
151 | | - | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
152 | 157 | | |
153 | 158 | | |
154 | 159 | | |
155 | 160 | | |
156 | 161 | | |
157 | 162 | | |
158 | 163 | | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
159 | 171 | | |
160 | 172 | | |
161 | 173 | | |
| |||
247 | 259 | | |
248 | 260 | | |
249 | 261 | | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
250 | 275 | | |
251 | 276 | | |
252 | 277 | | |
| |||
0 commit comments