diff --git a/internal/controller/api_vms.go b/internal/controller/api_vms.go index 1fc1ea7..3ff0fb5 100644 --- a/internal/controller/api_vms.go +++ b/internal/controller/api_vms.go @@ -312,26 +312,40 @@ func (controller *Controller) listVMs(ctx *gin.Context) responder.Responder { } } - return controller.storeView(func(txn storepkg.Transaction) responder.Responder { - allVMs, err := txn.ListVMs() + allVMs, err, _ := controller.single.Do("list-vms", func() (interface{}, error) { + var vms []v1.VM - if err != nil { - return responder.Error(err) - } + viewErr := controller.store.View(func(txn storepkg.Transaction) (err error) { + vms, err = txn.ListVMs() + return + }) - vms := make([]v1.VM, 0) - Outer: - for _, vm := range allVMs { - for _, filter := range filters { - if !vm.Match(filter) { - continue Outer - } - } - vms = append(vms, vm) - } - - return responder.JSON(http.StatusOK, vms) + return vms, viewErr }) + + if err != nil { + return responder.Error(err) + } + + vmList, ok := allVMs.([]v1.VM) + if !ok { + controller.logger.Errorf("failed to cast list-vms result to []v1.VM: %T", allVMs) + return responder.Code(http.StatusInternalServerError) + } + + vms := make([]v1.VM, 0, len(vmList)) + +Outer: + for _, vm := range vmList { + for _, filter := range filters { + if !vm.Match(filter) { + continue Outer + } + } + vms = append(vms, vm) + } + + return responder.JSON(http.StatusOK, vms) } func (controller *Controller) deleteVM(ctx *gin.Context) responder.Responder { diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 9ade7af..27384c3 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -29,6 +29,7 @@ import ( "golang.org/x/crypto/ssh" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" + "golang.org/x/sync/singleflight" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" ) @@ -73,6 +74,8 @@ type Controller struct { sshNoClientAuth bool sshServer *sshserver.SSHServer + single singleflight.Group + rpc.UnimplementedControllerServer } @@ -83,6 +86,7 @@ func New(opts ...Option) (*Controller, error) { workerOfflineTimeout: 3 * time.Minute, maxWorkersPerLicense: maxWorkersPerDefaultLicense, pingInterval: 30 * time.Second, + single: singleflight.Group{}, } // Apply options