Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Bolt's Journal

## 2026-03-09 - Lock-Free Pre-Allocated Map-Reduce for Multi-Regional Queries
**Learning:** Performing multi-regional queries (like `listAllRegions` across 24 GCP regions) concurrently using a shared slice protected by `sync.Mutex` causes lock contention, slice allocation churn, and heavy garbage collection overhead. Utilizing a lock-free, pre-allocated map-reduce pattern using a slice of slices (`[][]T`) eliminates mutex contention entirely, and pre-allocating the final result slice based on the exact sum of sizes of individual regional results reduces heap allocations considerably.
**Action:** Always favor lock-free, index-mapped pre-allocation arrays (`[][]T`) over shared mutex-protected slices (`[]T`) when collecting results concurrently from a known, fixed number of concurrent tasks.

## 2026-03-08 - GCP Logging Client Caching & Connection Longevity
**Learning:** Establishing the GCP Stackdriver Logging client requires repeated Google credential discovery and connection establishment, causing high latency (~300ms) inside a reactive TUI interface. Caching `logadmin.Client` instances via a project-aware map with thread-safe `sync.Mutex` ensures subsequent streaming and log extraction operations are instantaneous. Crucially, calling `Close()` on individual stream terminations must be a no-op to prevent premature teardown of connection pools shared across other active streaming views.
**Action:** Keep GCP Logging clients cached globally by project and handle connection termination via a no-op `Close` method, while adding test-isolation resets in unit tests.
Expand Down
2 changes: 1 addition & 1 deletion internal/run/api/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestWrapError(t *testing.T) {
assert.Error(t, got)
assert.Contains(t, got.Error(), tt.wantSubString)
if tt.wantWrapped {
assert.ErrorIs(t, got, tt.inputErr) // Checks if it wraps the original error
assert.ErrorIs(t, got, tt.inputErr) // Checks if it wraps the original error
assert.NotEqual(t, tt.inputErr, got) // Should not be exactly the same object (wrapped)
} else {
assert.Equal(t, tt.inputErr, got) // Should be exactly the same
Expand Down
4 changes: 2 additions & 2 deletions internal/run/api/domainmapping/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,11 @@ func (c *GCPClient) ListDomainMappings(ctx context.Context, project, region stri
} else {
pageToken = ""
}

if pageToken == "" {
break
}
}

return domainMappings, nil
}
}
44 changes: 27 additions & 17 deletions internal/run/api/domainmapping/domainmapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import (
"sync"
"time"

"google.golang.org/api/run/v1"
api_region "github.com/JulienBreux/run-cli/internal/run/api/region"
model "github.com/JulienBreux/run-cli/internal/run/model/domainmapping"
"github.com/JulienBreux/run-cli/internal/run/model/common/condition"
model "github.com/JulienBreux/run-cli/internal/run/model/domainmapping"
"google.golang.org/api/run/v1"
)

var apiClient Client = &GCPClient{}
Expand All @@ -50,25 +50,35 @@ func List(project, region string) ([]model.DomainMapping, error) {
}

func listAllRegions(project string) ([]model.DomainMapping, error) {
var (
mu sync.Mutex
domainMappings []model.DomainMapping
wg sync.WaitGroup
)

for _, region := range api_region.List() {
// Optimization: lock-free, pre-allocated map-reduce pattern with a slice of slices [][]model.DomainMapping.
// This eliminates mutex contention across concurrent region requests, avoids repeated slice resizing/heap allocations,
// and optimizes concurrency to reduce latency and memory usage.
regions := api_region.List()
results := make([][]model.DomainMapping, len(regions))
var wg sync.WaitGroup

for i, region := range regions {
wg.Add(1)
go func(r string) {
go func(idx int, r string) {
defer wg.Done()
if dms, err := List(project, r); err == nil {
mu.Lock()
domainMappings = append(domainMappings, dms...)
mu.Unlock()
results[idx] = dms
}
}(region)
}(i, region)
}

wg.Wait()

total := 0
for _, dms := range results {
total += len(dms)
}

domainMappings := make([]model.DomainMapping, 0, total)
for _, dms := range results {
domainMappings = append(domainMappings, dms...)
}

return domainMappings, nil
}

Expand All @@ -95,12 +105,12 @@ func mapDomainMapping(resp *run.DomainMapping, project, region string) model.Dom
})
}
}

routeName := ""
if resp.Spec != nil {
routeName = resp.Spec.RouteName
}

createTime := time.Time{}
name := ""
creator := ""
Expand All @@ -127,4 +137,4 @@ func mapDomainMapping(resp *run.DomainMapping, project, region string) model.Dom
CreateTime: createTime,
Conditions: conditions,
}
}
}
8 changes: 4 additions & 4 deletions internal/run/api/domainmapping/domainmapping_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func TestList_AllRegions(t *testing.T) {

dms, err := List("p", "all")
assert.NoError(t, err)

// Since api_region.List() returns many regions, we just want to ensure we called List for them and aggregated results.
// We mocked return for "us-central1".
found := false
Expand Down Expand Up @@ -212,7 +212,7 @@ func TestGCPClient_ListDomainMappings(t *testing.T) {
ListFunc: func(parent string, pageToken string) (*run.ListDomainMappingsResponse, error) {
if pageToken == "" {
return &run.ListDomainMappingsResponse{
Items: []*run.DomainMapping{{Metadata: &run.ObjectMeta{Name: "dm1"}}},
Items: []*run.DomainMapping{{Metadata: &run.ObjectMeta{Name: "dm1"}}},
Metadata: &run.ListMeta{Continue: "next-page"},
}, nil
}
Expand Down Expand Up @@ -245,7 +245,7 @@ func TestGCPClient_ListDomainMappings(t *testing.T) {
})

t.Run("ClientCreationError", func(t *testing.T) {
// Reset auth mock for this test
// Reset auth mock for this test
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
}
Expand All @@ -259,7 +259,7 @@ func TestGCPClient_ListDomainMappings(t *testing.T) {
})

t.Run("ListError", func(t *testing.T) {
// Reset auth mock for this test
// Reset auth mock for this test
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
}
Expand Down
8 changes: 4 additions & 4 deletions internal/run/api/job/execution/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func mapExecution(resp *runpb.Execution, region string) model.Execution {
// We can try to find the "Completed" or "Succeeded" condition or check terminal condition logic if exposed directly.
// runpb.Execution doesn't have a direct TerminalCondition field like Job, but it has Conditions.
// We usually look for "Completed" type.

// Helper to find latest relevant condition or just map all of them.
var conditions []*condition.Condition
for _, c := range resp.Conditions {
Expand All @@ -66,7 +66,7 @@ func mapExecution(resp *runpb.Execution, region string) model.Execution {
LastTransitionTime: c.LastTransitionTime.AsTime(),
}
conditions = append(conditions, cond)

// Heuristic: If condition is "Completed", treat as terminal status for summary
if c.Type == "Completed" {
terminalCondition = cond
Expand Down Expand Up @@ -151,10 +151,10 @@ func (c *GCPClient) ListExecutions(ctx context.Context, project, region, jobName
// BUT, executions are children of Jobs? No, they are children of Location.
// The resource name is projects/*/locations/*/jobs/*/executions/ -> v1
// v2: projects/*/locations/*/jobs/*/executions

// Wait, `runpb.ListExecutionsRequest` expects Parent = `projects/{project}/locations/{location}/jobs/{job}` OR `projects/{project}/locations/{location}`.
// If we can pass the job as parent, we get filtered list!

parent := jobName
if !strings.HasPrefix(jobName, "projects/") {
parent = fmt.Sprintf("projects/%s/locations/%s/jobs/%s", project, region, jobName)
Expand Down
34 changes: 22 additions & 12 deletions internal/run/api/job/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,27 +79,37 @@ func mapJob(resp *runpb.Job, region string) model.Job {
}

func listAllRegions(project string) ([]model.Job, error) {
var (
mu sync.Mutex
jobs []model.Job
wg sync.WaitGroup
)

for _, region := range api_region.List() {
// Optimization: lock-free, pre-allocated map-reduce pattern with a slice of slices [][]model.Job.
// This eliminates mutex contention across concurrent region requests, avoids repeated slice resizing/heap allocations,
// and optimizes concurrency to reduce latency and memory usage.
regions := api_region.List()
results := make([][]model.Job, len(regions))
var wg sync.WaitGroup

for i, region := range regions {
wg.Add(1)
go func(r string) {
go func(idx int, r string) {
defer wg.Done()
// Call List recursively for each region
// We ignore errors here to allow partial success (e.g. if one region is down or disabled)
if j, err := List(project, r); err == nil {
mu.Lock()
jobs = append(jobs, j...)
mu.Unlock()
results[idx] = j
}
}(region)
}(i, region)
}

wg.Wait()

total := 0
for _, j := range results {
total += len(j)
}

jobs := make([]model.Job, 0, total)
for _, j := range results {
jobs = append(jobs, j...)
}

return jobs, nil
}

Expand Down
30 changes: 15 additions & 15 deletions internal/run/api/job/job_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ func TestMapJob(t *testing.T) {
assert.Equal(t, resp.Name, result.Name)
assert.Equal(t, "user@example.com", result.Creator)
assert.Equal(t, "us-central1", result.Region)

// Execution
assert.NotNil(t, result.LatestCreatedExecution)
assert.Equal(t, resp.LatestCreatedExecution.Name, result.LatestCreatedExecution.Name)
assert.Equal(t, now.Unix(), result.LatestCreatedExecution.CreateTime.Unix())

// Condition
assert.NotNil(t, result.TerminalCondition)
assert.Equal(t, "CONDITION_SUCCEEDED", result.TerminalCondition.State)
Expand Down Expand Up @@ -186,7 +186,7 @@ func TestList_AllRegions(t *testing.T) {

jobs, err := List("p", api_region.ALL)
assert.NoError(t, err)

found := false
for _, j := range jobs {
if j.Name == "job-us" && j.Region == "us-central1" {
Expand Down Expand Up @@ -295,7 +295,7 @@ func TestGCPClient_ListJobs(t *testing.T) {
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to find default credentials")
})

t.Run("Client Creation Error", func(t *testing.T) {
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
Expand Down Expand Up @@ -328,7 +328,7 @@ func TestGCPClient_ListJobs(t *testing.T) {
assert.Error(t, err)
assert.Contains(t, err.Error(), "iter error")
})

t.Run("Iterator Auth Error", func(t *testing.T) {
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
Expand Down Expand Up @@ -357,7 +357,7 @@ func TestGCPClient_RunJob(t *testing.T) {
client.FindDefaultCredentials = origFindCreds
createJobsClient = origCreateClient
}()

client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return &google.Credentials{}, nil
}
Expand All @@ -375,13 +375,13 @@ func TestGCPClient_RunJob(t *testing.T) {
CloseFunc: func() error { return nil },
}, nil
}

client := &GCPClient{}
exec, err := client.RunJob(context.Background(), "job1")
assert.NoError(t, err)
assert.Equal(t, "exec-1", exec.Name)
})

t.Run("Run Error", func(t *testing.T) {
createJobsClient = func(ctx context.Context, opts ...option.ClientOption) (JobsClientWrapper, error) {
return &MockJobsClientWrapper{
Expand All @@ -391,18 +391,18 @@ func TestGCPClient_RunJob(t *testing.T) {
CloseFunc: func() error { return nil },
}, nil
}

client := &GCPClient{}
_, err := client.RunJob(context.Background(), "job1")
assert.Error(t, err)
assert.Contains(t, err.Error(), "run failed")
})

t.Run("Client Creation Error", func(t *testing.T) {
createJobsClient = func(ctx context.Context, opts ...option.ClientOption) (JobsClientWrapper, error) {
return nil, errors.New("client creation error")
}

client := &GCPClient{}
_, err := client.RunJob(context.Background(), "job1")
assert.Error(t, err)
Expand All @@ -412,21 +412,21 @@ func TestGCPClient_RunJob(t *testing.T) {

func TestWrappers_Delegation(t *testing.T) {
// Expect panics because nil clients are used

t.Run("GCPJobsClientWrapper", func(t *testing.T) {
w := &GCPJobsClientWrapper{client: nil}
assert.Panics(t, func() { _ = w.ListJobs(context.Background(), nil) })
assert.Panics(t, func() { _, _ = w.RunJob(context.Background(), nil) })
assert.Panics(t, func() { _ = w.Close() })
})

t.Run("GCPJobIteratorWrapper", func(t *testing.T) {
it := &GCPJobIteratorWrapper{it: nil}
assert.Panics(t, func() { _, _ = it.Next() })
})

t.Run("GCPRunJobOperationWrapper", func(t *testing.T) {
op := &GCPRunJobOperationWrapper{op: nil}
assert.Panics(t, func() { _, _ = op.Wait(context.Background()) })
})
}
}
12 changes: 6 additions & 6 deletions internal/run/api/log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func TestGCPClient(t *testing.T) {
entry, err := it.Next()
assert.NoError(t, err)
assert.Equal(t, "log1", entry.Payload)

assert.NoError(t, client.Close())
})

Expand All @@ -293,7 +293,7 @@ func TestGCPClient(t *testing.T) {
client.FindDefaultCredentials = func(ctx context.Context, scopes ...string) (*google.Credentials, error) {
return nil, errors.New("auth failed")
}

_, err := NewGCPClient(context.Background(), "project")
assert.Error(t, err)
assert.Contains(t, err.Error(), "failed to find default credentials")
Expand All @@ -307,7 +307,7 @@ func TestGCPClient(t *testing.T) {
createLogAdminClient = func(ctx context.Context, projectID string, opts ...option.ClientOption) (LogAdminClientWrapper, error) {
return nil, errors.New("creation failed")
}

_, err := NewGCPClient(context.Background(), "project")
assert.Error(t, err)
assert.Contains(t, err.Error(), "creation failed")
Expand Down Expand Up @@ -355,15 +355,15 @@ func TestGCPClient(t *testing.T) {

func TestWrappers_Delegation(t *testing.T) {
// Expect panics because nil clients are used

t.Run("RealLogAdminClient", func(t *testing.T) {
w := &RealLogAdminClient{client: nil}
assert.Panics(t, func() { _ = w.Entries(context.Background()) })
assert.Panics(t, func() { _ = w.Close() })
})

t.Run("GCPEntryIterator", func(t *testing.T) {
it := &GCPEntryIterator{it: nil}
assert.Panics(t, func() { _, _ = it.Next() })
})
}
}
Loading
Loading