Skip to content

Commit f3fa344

Browse files
committed
Expose skipped ownership conflicts during PostgreSQL pushes
1 parent b92afbc commit f3fa344

4 files changed

Lines changed: 118 additions & 14 deletions

File tree

cmd/agentsview/pg.go

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/base64"
66
"errors"
77
"fmt"
8+
"io"
89
"log"
910
"os"
1011
"os/signal"
@@ -112,27 +113,60 @@ func runPGPush(cfg PGPushConfig) {
112113
fmt.Println("Starting PostgreSQL push...")
113114
result, err := ps.Push(ctx, forceFull,
114115
func(p postgres.PushProgress) {
115-
fmt.Printf(
116-
"\rPushing... %d/%d sessions, %d messages",
117-
p.SessionsDone, p.SessionsTotal,
118-
p.MessagesDone,
119-
)
116+
printPGPushProgress(p)
120117
},
121118
)
122119
fmt.Print("\r\033[K") // clear progress line
123120
if err != nil {
124121
fatal("pg push: %v", err)
125122
}
123+
writePGPushSummary(os.Stdout, result)
124+
if result.Errors > 0 {
125+
fatal("pg push: %d session(s) failed",
126+
result.Errors)
127+
}
128+
}
129+
130+
func printPGPushProgress(p postgres.PushProgress) {
131+
if p.SkippedConflicts > 0 {
132+
fmt.Printf(
133+
"\rPushing... %d/%d sessions, %d messages, %d ownership conflicts skipped",
134+
p.SessionsDone, p.SessionsTotal,
135+
p.MessagesDone, p.SkippedConflicts,
136+
)
137+
return
138+
}
126139
fmt.Printf(
140+
"\rPushing... %d/%d sessions, %d messages",
141+
p.SessionsDone, p.SessionsTotal,
142+
p.MessagesDone,
143+
)
144+
}
145+
146+
func writePGPushSummary(w io.Writer, result postgres.PushResult) {
147+
if result.SkippedConflicts > 0 {
148+
fmt.Fprintf(
149+
w,
150+
"Pushed %d sessions, %d messages, skipped %d ownership conflict(s) in %s\n",
151+
result.SessionsPushed,
152+
result.MessagesPushed,
153+
result.SkippedConflicts,
154+
result.Duration.Round(time.Millisecond),
155+
)
156+
fmt.Fprintf(
157+
w,
158+
"Warning: skipped %d session(s) owned by another PostgreSQL push marker\n",
159+
result.SkippedConflicts,
160+
)
161+
return
162+
}
163+
fmt.Fprintf(
164+
w,
127165
"Pushed %d sessions, %d messages in %s\n",
128166
result.SessionsPushed,
129167
result.MessagesPushed,
130168
result.Duration.Round(time.Millisecond),
131169
)
132-
if result.Errors > 0 {
133-
fatal("pg push: %d session(s) failed",
134-
result.Errors)
135-
}
136170
}
137171

138172
func runPGStatus() {

cmd/agentsview/pg_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
package main
22

33
import (
4+
"bytes"
45
"os"
56
"os/exec"
67
"path/filepath"
78
"strings"
89
"testing"
10+
"time"
911

1012
"github.com/stretchr/testify/assert"
1113
"github.com/stretchr/testify/require"
1214
"go.kenn.io/agentsview/internal/config"
15+
"go.kenn.io/agentsview/internal/postgres"
1316
)
1417

1518
func loadPGServeConfigForTest(t *testing.T, args ...string) (config.Config, string, error) {
@@ -166,3 +169,20 @@ func TestRunPGServeHelperProcess(t *testing.T) {
166169
require.NoError(t, err)
167170
runPGServe(cfg, basePath)
168171
}
172+
173+
func TestWritePGPushSummaryIncludesSkippedConflicts(t *testing.T) {
174+
var out bytes.Buffer
175+
176+
writePGPushSummary(&out, postgres.PushResult{
177+
SessionsPushed: 3,
178+
MessagesPushed: 9,
179+
SkippedConflicts: 2,
180+
Duration: 1500 * time.Millisecond,
181+
})
182+
183+
got := out.String()
184+
assert.Contains(t, got,
185+
"Pushed 3 sessions, 9 messages, skipped 2 ownership conflict(s) in 1.5s")
186+
assert.Contains(t, got,
187+
"Warning: skipped 2 session(s) owned by another PostgreSQL push marker")
188+
}

cmd/agentsview/pg_watch.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,42 @@ func (p *pgPusher) push(
6464
return fmt.Errorf("push: %w", err)
6565
}
6666
if res.Errors > 0 {
67-
log.Printf(
68-
"pg watch: pushed %d sessions, %d messages, %d errors (%s)",
69-
res.SessionsPushed, res.MessagesPushed, res.Errors, reason,
70-
)
67+
logPGWatchPushResult(res, reason)
7168
log.Printf(
7269
"pg watch: %d session(s) failed to push; will retry",
7370
res.Errors,
7471
)
7572
return nil
7673
}
74+
logPGWatchPushResult(res, reason)
75+
return nil
76+
}
77+
78+
func logPGWatchPushResult(res postgres.PushResult, reason pushReason) {
79+
if res.SkippedConflicts > 0 {
80+
log.Printf(
81+
"pg watch: pushed %d sessions, %d messages, skipped %d ownership conflict(s), %d errors (%s)",
82+
res.SessionsPushed, res.MessagesPushed,
83+
res.SkippedConflicts, res.Errors, reason,
84+
)
85+
log.Printf(
86+
"pg watch: %d session(s) skipped due to PostgreSQL ownership conflicts",
87+
res.SkippedConflicts,
88+
)
89+
return
90+
}
91+
if res.Errors > 0 {
92+
log.Printf(
93+
"pg watch: pushed %d sessions, %d messages, %d errors (%s)",
94+
res.SessionsPushed, res.MessagesPushed,
95+
res.Errors, reason,
96+
)
97+
return
98+
}
7799
log.Printf(
78100
"pg watch: pushed %d sessions, %d messages (%s)",
79101
res.SessionsPushed, res.MessagesPushed, reason,
80102
)
81-
return nil
82103
}
83104

84105
func (p *pgPusher) reset() {

cmd/agentsview/pg_watch_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,35 @@ func TestPgPusher_LogsPartialPushErrors(t *testing.T) {
248248
assert.Contains(t, got, "change")
249249
}
250250

251+
func TestPgPusher_LogsSkippedConflicts(t *testing.T) {
252+
target := &fakeTarget{
253+
pushResult: postgres.PushResult{
254+
SessionsPushed: 3,
255+
MessagesPushed: 9,
256+
SkippedConflicts: 2,
257+
},
258+
}
259+
var logs bytes.Buffer
260+
prev := log.Writer()
261+
log.SetOutput(&logs)
262+
t.Cleanup(func() { log.SetOutput(prev) })
263+
264+
p := &pgPusher{
265+
localSync: func(context.Context) error { return nil },
266+
connect: func() (pgTarget, error) {
267+
return target, nil
268+
},
269+
}
270+
require.NoError(t, p.push(context.Background(), reasonChange, false))
271+
272+
got := logs.String()
273+
assert.Contains(t, got,
274+
"pushed 3 sessions, 9 messages, skipped 2 ownership conflict(s), 0 errors")
275+
assert.Contains(t, got,
276+
"2 session(s) skipped due to PostgreSQL ownership conflicts")
277+
assert.Contains(t, got, "change")
278+
}
279+
251280
func TestResolveWatchTargets_ErrorsOnEmptyURL(t *testing.T) {
252281
appCfg := config.Config{} // no PG URL
253282
_, _, _, err := resolveWatchTargets(appCfg, PGPushConfig{})

0 commit comments

Comments
 (0)