Skip to content

Commit be869f1

Browse files
fkorotkovoai-codex
andauthored
Refactor listing VMs (#399)
* Removed unnesesary ListOptions * Refactor genericList to accept string prefixes instead of byte slices * Optimize VM listing logic with singleflight to deduplicate concurrent request * Refactor VM listing logic: rename variables for clarity and update error messages * fix: address PR review feedback - use singleflight DoChan with context cancellation for list VMs 🤖 Generated with [Codex](https://chatgpt.com/codex) Co-Authored-By: Codex <codex@openai.com> --------- Co-authored-by: Codex <codex@openai.com>
1 parent 230a83c commit be869f1

File tree

8 files changed

+53
-54
lines changed

8 files changed

+53
-54
lines changed

internal/controller/api_vms.go

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,9 @@ func (controller *Controller) listVMs(ctx *gin.Context) responder.Responder {
299299
return responder
300300
}
301301

302-
var opts []storepkg.ListOption
302+
var filters []v1.Filter
303303

304304
if filterRaw := ctx.Query("filter"); filterRaw != "" {
305-
var filters []v1.Filter
306-
307305
for _, filterRaw := range strings.Split(filterRaw, ",") {
308306
filter, err := v1.NewFilter(filterRaw)
309307
if err != nil {
@@ -312,23 +310,53 @@ func (controller *Controller) listVMs(ctx *gin.Context) responder.Responder {
312310

313311
filters = append(filters, filter)
314312
}
313+
}
315314

316-
if len(filters) > 1 {
317-
return responder.JSON(http.StatusPreconditionFailed, NewErrorResponse("only "+
318-
"a single filter is currently supported"))
319-
}
315+
resultCh := controller.single.DoChan("list-vms", func() (interface{}, error) {
316+
var vms []v1.VM
317+
318+
viewErr := controller.store.View(func(txn storepkg.Transaction) (err error) {
319+
vms, err = txn.ListVMs()
320+
return
321+
})
322+
323+
return vms, viewErr
324+
})
320325

321-
opts = append(opts, storepkg.WithListFilters(filters...))
326+
var computedVMs interface{}
327+
var err error
328+
329+
select {
330+
case <-ctx.Done():
331+
return responder.Empty()
332+
case result := <-resultCh:
333+
computedVMs = result.Val
334+
err = result.Err
322335
}
323336

324-
return controller.storeView(func(txn storepkg.Transaction) responder.Responder {
325-
vms, err := txn.ListVMs(opts...)
326-
if err != nil {
327-
return responder.Error(err)
337+
if err != nil {
338+
return responder.Error(err)
339+
}
340+
341+
allVMs, ok := computedVMs.([]v1.VM)
342+
if !ok {
343+
controller.logger.Errorf("failed to compute vms: %T", computedVMs)
344+
return responder.Code(http.StatusInternalServerError)
345+
}
346+
347+
vms := make([]v1.VM, 0, len(allVMs))
348+
349+
Outer:
350+
for _, vm := range allVMs {
351+
for _, filter := range filters {
352+
if !vm.Match(filter) {
353+
continue Outer
354+
}
328355
}
356+
vms = append(vms, vm)
357+
}
329358

330-
return responder.JSON(http.StatusOK, vms)
331-
})
359+
return responder.JSON(http.StatusOK, vms)
332360
}
333361

334362
func (controller *Controller) deleteVM(ctx *gin.Context) responder.Responder {

internal/controller/controller.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"golang.org/x/crypto/ssh"
3030
"golang.org/x/net/http2"
3131
"golang.org/x/net/http2/h2c"
32+
"golang.org/x/sync/singleflight"
3233
"google.golang.org/grpc"
3334
"google.golang.org/grpc/keepalive"
3435
)
@@ -73,6 +74,8 @@ type Controller struct {
7374
sshNoClientAuth bool
7475
sshServer *sshserver.SSHServer
7576

77+
single singleflight.Group
78+
7679
rpc.UnimplementedControllerServer
7780
}
7881

@@ -83,6 +86,7 @@ func New(opts ...Option) (*Controller, error) {
8386
workerOfflineTimeout: 3 * time.Minute,
8487
maxWorkersPerLicense: maxWorkersPerDefaultLicense,
8588
pingInterval: 30 * time.Second,
89+
single: singleflight.Group{},
8690
}
8791

8892
// Apply options

internal/controller/store/badger/badger_generic.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package badger
33
import (
44
"encoding/json"
55

6-
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
7-
v1 "github.com/cirruslabs/orchard/pkg/resource/v1"
86
"github.com/dgraph-io/badger/v3"
97
)
108

@@ -53,30 +51,21 @@ func genericGet[T any, PT interface {
5351

5452
func genericList[T any, PT interface {
5553
SetVersion(uint64)
56-
Match(v1.Filter) bool
5754
*T
58-
}](txn *Transaction, prefix []byte, opts ...storepkg.ListOption) (_ []T, err error) {
55+
}](txn *Transaction, prefix string) (_ []T, err error) {
5956
defer func() {
6057
err = mapErr(err)
6158
}()
6259

63-
// Apply options
64-
listInput := &storepkg.ListInput{}
65-
66-
for _, opt := range opts {
67-
opt(listInput)
68-
}
69-
7060
// Declare an empty, non-nil slice to
7161
// return [] when no objects are found
7262
result := []T{}
7363

7464
it := txn.badgerTxn.NewIterator(badger.IteratorOptions{
75-
Prefix: prefix,
65+
Prefix: []byte(prefix),
7666
})
7767
defer it.Close()
7868

79-
Outer:
8069
for it.Rewind(); it.Valid(); it.Next() {
8170
item := it.Item()
8271

@@ -91,12 +80,6 @@ Outer:
9180
return nil, err
9281
}
9382

94-
for _, filter := range listInput.Filters {
95-
if !PT(&obj).Match(filter) {
96-
continue Outer
97-
}
98-
}
99-
10083
PT(&obj).SetVersion(item.Version())
10184

10285
result = append(result, obj)

internal/controller/store/badger/badger_service_account.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ func (txn *Transaction) DeleteServiceAccount(name string) error {
2525
}
2626

2727
func (txn *Transaction) ListServiceAccounts() ([]v1.ServiceAccount, error) {
28-
return genericList[v1.ServiceAccount](txn, []byte(SpaceServiceAccounts))
28+
return genericList[v1.ServiceAccount](txn, SpaceServiceAccounts)
2929
}

internal/controller/store/badger/badger_vm.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ package badger
44
import (
55
"path"
66

7-
storepkg "github.com/cirruslabs/orchard/internal/controller/store"
87
"github.com/cirruslabs/orchard/pkg/resource/v1"
98
)
109

@@ -26,6 +25,6 @@ func (txn *Transaction) DeleteVM(name string) error {
2625
return genericDelete(txn, VMKey(name))
2726
}
2827

29-
func (txn *Transaction) ListVMs(opts ...storepkg.ListOption) ([]v1.VM, error) {
30-
return genericList[v1.VM](txn, []byte(SpaceVMs), opts...)
28+
func (txn *Transaction) ListVMs() ([]v1.VM, error) {
29+
return genericList[v1.VM](txn, SpaceVMs)
3130
}

internal/controller/store/badger/badger_worker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ func (txn *Transaction) DeleteWorker(name string) error {
2626
}
2727

2828
func (txn *Transaction) ListWorkers() ([]v1.Worker, error) {
29-
return genericList[v1.Worker](txn, []byte(SpaceWorkers))
29+
return genericList[v1.Worker](txn, SpaceWorkers)
3030
}

internal/controller/store/option.go

Lines changed: 0 additions & 15 deletions
This file was deleted.

internal/controller/store/store.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type Transaction interface {
2929
GetVM(name string) (result *v1.VM, err error)
3030
SetVM(vm v1.VM) (err error)
3131
DeleteVM(name string) (err error)
32-
ListVMs(opts ...ListOption) (result []v1.VM, err error)
32+
ListVMs() (result []v1.VM, err error)
3333

3434
GetWorker(name string) (result *v1.Worker, err error)
3535
SetWorker(worker v1.Worker) (err error)

0 commit comments

Comments
 (0)