Skip to content

Commit d412879

Browse files
author
Nathan Gillett
committed
fix(sdk-go): log Wrap record failures instead of panicking
When the wrapped function succeeds but outbox recording fails, return the result and log the infrastructure error. Panic paths still attach recording failures before re-raising. Signed-off-by: Nathan Gillett <nathan@intentproof.io>
1 parent 687874c commit d412879

3 files changed

Lines changed: 60 additions & 8 deletions

File tree

intentproof/instrumentation.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ package intentproof
22

33
import (
44
"fmt"
5+
"log"
56
"time"
67

78
"github.com/oklog/ulid/v2"
89
)
910

11+
var logExecutionRecordFailure = func(err error) {
12+
log.Printf("[intentproof] execution record failed: %v", err)
13+
}
14+
1015
// RunWithCorrelationID runs fn with correlation_id set for nested wrap() calls.
1116
func RunWithCorrelationID(correlationID string, fn func()) {
1217
runWithCorrelationID(correlationID, fn)
@@ -95,7 +100,9 @@ func recordExecution(
95100
}
96101

97102
// Wrap instruments fn to emit a signed ExecutionEvent.v1 on each call.
98-
// Panics from fn are re-raised after recording an error event.
103+
// Panics from fn are re-raised after recording an error event. When fn
104+
// returns normally but recording fails, the result is still returned and
105+
// the failure is logged; use WrapFunc to surface recording errors.
99106
func Wrap[T, R any](intent, action string, fn func(T) R) func(T) R {
100107
return func(arg T) (result R) {
101108
t0 := time.Now().UnixMilli()
@@ -130,7 +137,7 @@ func Wrap[T, R any](intent, action string, fn func(T) R) func(T) R {
130137
if didPanic {
131138
panic(fmt.Errorf("%v: %w", panicVal, recErr))
132139
}
133-
panic(recErr)
140+
logExecutionRecordFailure(recErr)
134141
}
135142
if didPanic {
136143
panic(panicVal)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package intentproof
2+
3+
import (
4+
"path/filepath"
5+
"sync/atomic"
6+
"testing"
7+
)
8+
9+
func TestWrapLogsRecordFailureOnSuccessPath(t *testing.T) {
10+
dir := t.TempDir()
11+
dbPath := filepath.Join(dir, "outbox.db")
12+
dataDir := filepath.Join(dir, "data")
13+
if err := Configure(ConfigureOptions{
14+
DBPath: dbPath,
15+
DataDir: dataDir,
16+
TenantID: "tnt_log_rec",
17+
}); err != nil {
18+
t.Fatal(err)
19+
}
20+
ob, err := GetOutbox()
21+
if err != nil {
22+
t.Fatal(err)
23+
}
24+
if err := ob.Close(); err != nil {
25+
t.Fatal(err)
26+
}
27+
28+
var logged atomic.Bool
29+
prev := logExecutionRecordFailure
30+
logExecutionRecordFailure = func(err error) {
31+
logged.Store(true)
32+
if err == nil {
33+
t.Fatal("expected record error")
34+
}
35+
}
36+
defer func() { logExecutionRecordFailure = prev }()
37+
38+
fn := Wrap("Test", "test.action", func(x int) int { return x + 1 })
39+
if got := fn(2); got != 3 {
40+
t.Fatalf("result: got %d want 3", got)
41+
}
42+
if !logged.Load() {
43+
t.Fatal("expected record failure to be logged")
44+
}
45+
}

intentproof/sdk_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func TestConcurrentWrapPreservesChainPositions(t *testing.T) {
158158
}
159159
}
160160

161-
func TestWrapRecordFailureOnSuccessPathDoesNotMisclassify(t *testing.T) {
161+
func TestWrapRecordFailureOnSuccessPathReturnsResult(t *testing.T) {
162162
dbPath, dataDir := testDirs(t)
163163
configureTest(t, dbPath, dataDir, "tnt_a")
164164
ob, err := intentproof.GetOutbox()
@@ -170,17 +170,17 @@ func TestWrapRecordFailureOnSuccessPathDoesNotMisclassify(t *testing.T) {
170170
}
171171

172172
fn := intentproof.Wrap("Test", "test.action", func(x int) int { return x * 2 })
173-
panicked := false
173+
var got int
174174
func() {
175175
defer func() {
176176
if recover() != nil {
177-
panicked = true
177+
t.Fatal("unexpected panic on success-path record failure")
178178
}
179179
}()
180-
intentproof.RunWithCorrelationID("corr-rec-fail", func() { fn(3) })
180+
intentproof.RunWithCorrelationID("corr-rec-fail", func() { got = fn(3) })
181181
}()
182-
if !panicked {
183-
t.Fatal("expected panic from record failure")
182+
if got != 6 {
183+
t.Fatalf("result: got %d want 6", got)
184184
}
185185

186186
ob2, err := intentproof.OpenOutbox(dbPath)

0 commit comments

Comments
 (0)