Skip to content

Commit dbf0a41

Browse files
committed
feat: Add per-service scaling and gRPC support
Signed-off-by: Jatin Kumar <jatink.5251@gmail.com>
1 parent 033287d commit dbf0a41

13 files changed

+562
-515
lines changed

infra/feast-operator/api/v1/featurestore_types.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ const (
3030
FailedPhase = "Failed"
3131

3232
// Feast condition types:
33-
ClientReadyType = "Client"
34-
OfflineStoreReadyType = "OfflineStore"
35-
OnlineStoreReadyType = "OnlineStore"
33+
ClientReadyType = "Client"
34+
OfflineStoreReadyType = "OfflineStore"
35+
OnlineStoreReadyType = "OnlineStore"
3636
OnlineStoreGrpcReadyType = "OnlineStoreGrpc"
37-
RegistryReadyType = "Registry"
38-
UIReadyType = "UI"
39-
ReadyType = "FeatureStore"
40-
AuthorizationReadyType = "Authorization"
41-
CronJobReadyType = "CronJob"
37+
RegistryReadyType = "Registry"
38+
UIReadyType = "UI"
39+
ReadyType = "FeatureStore"
40+
AuthorizationReadyType = "Authorization"
41+
CronJobReadyType = "CronJob"
4242

4343
// Feast condition reasons:
4444
ReadyReason = "Ready"
@@ -77,9 +77,9 @@ type FeatureStoreSpec struct {
7777
FeastProjectDir *FeastProjectDir `json:"feastProjectDir,omitempty"`
7878
Services *FeatureStoreServices `json:"services,omitempty"`
7979
// FeatureServer configures the Feast feature server, including MCP support.
80-
FeatureServer *FeatureServerConfig `json:"feature_server,omitempty"`
81-
AuthzConfig *AuthzConfig `json:"authz,omitempty"`
82-
CronJob *FeastCronJob `json:"cronJob,omitempty"`
80+
FeatureServer *FeatureServerConfig `json:"feature_server,omitempty"`
81+
AuthzConfig *AuthzConfig `json:"authz,omitempty"`
82+
CronJob *FeastCronJob `json:"cronJob,omitempty"`
8383
}
8484

8585
// FeatureServerConfig defines feature server configuration settings.
@@ -387,7 +387,7 @@ var ValidOfflineStoreDBStorePersistenceTypes = []string{
387387
// OnlineStore configures the online store service
388388
type OnlineStore struct {
389389
// Creates a feature server container
390-
Server *ServerConfigs `json:"server,omitempty"`
390+
Server *ServerConfigs `json:"server,omitempty"`
391391
// Creates a gRPC feature server container (feast listen)
392392
Grpc *GrpcServerConfigs `json:"grpc,omitempty"`
393393
Persistence *OnlineStorePersistence `json:"persistence,omitempty"`
@@ -742,12 +742,12 @@ type FeatureStoreStatus struct {
742742

743743
// ServiceHostnames defines the service hostnames in the format of <domain>:<port>, e.g. example.svc.cluster.local:80
744744
type ServiceHostnames struct {
745-
OfflineStore string `json:"offlineStore,omitempty"`
746-
OnlineStore string `json:"onlineStore,omitempty"`
745+
OfflineStore string `json:"offlineStore,omitempty"`
746+
OnlineStore string `json:"onlineStore,omitempty"`
747747
OnlineStoreGrpc string `json:"onlineStoreGrpc,omitempty"`
748-
Registry string `json:"registry,omitempty"`
749-
RegistryRest string `json:"registryRest,omitempty"`
750-
UI string `json:"ui,omitempty"`
748+
Registry string `json:"registry,omitempty"`
749+
RegistryRest string `json:"registryRest,omitempty"`
750+
UI string `json:"ui,omitempty"`
751751
}
752752

753753
// +kubebuilder:object:root=true

infra/feast-operator/internal/controller/featurestore_controller_db_store_test.go

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -429,17 +429,18 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
429429

430430
Expect(resource.Status.Phase).To(Equal(feastdevv1.PendingPhase))
431431

432-
// check deployment
433-
deploy := &appsv1.Deployment{}
434-
objMeta := feast.GetObjectMeta()
435-
err = k8sClient.Get(ctx, types.NamespacedName{
436-
Name: objMeta.Name,
437-
Namespace: objMeta.Namespace,
438-
}, deploy)
439-
Expect(err).NotTo(HaveOccurred())
440-
Expect(deploy.Spec.Replicas).To(Equal(int32Ptr(1)))
441-
Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue())
442-
Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(4))
432+
for _, feastType := range []services.FeastServiceType{
433+
services.RegistryFeastType,
434+
services.OfflineFeastType,
435+
services.OnlineFeastType,
436+
services.UIFeastType,
437+
} {
438+
deploy, err := getDeploymentByType(ctx, k8sClient, resource, feastType)
439+
Expect(err).NotTo(HaveOccurred())
440+
Expect(deploy.Spec.Replicas).To(Equal(int32Ptr(1)))
441+
Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue())
442+
Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1))
443+
}
443444
svc := &corev1.Service{}
444445
err = k8sClient.Get(ctx, types.NamespacedName{
445446
Name: feast.GetFeastServiceName(services.RegistryFeastType),
@@ -528,7 +529,7 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
528529
deployList := appsv1.DeploymentList{}
529530
err = k8sClient.List(ctx, &deployList, listOpts)
530531
Expect(err).NotTo(HaveOccurred())
531-
Expect(deployList.Items).To(HaveLen(1))
532+
Expect(deployList.Items).To(HaveLen(4))
532533

533534
svcList := corev1.ServiceList{}
534535
err = k8sClient.List(ctx, &svcList, listOpts)
@@ -549,15 +550,9 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
549550
},
550551
}
551552

552-
// check deployment
553-
deploy := &appsv1.Deployment{}
554-
objMeta := feast.GetObjectMeta()
555-
err = k8sClient.Get(ctx, types.NamespacedName{
556-
Name: objMeta.Name,
557-
Namespace: objMeta.Namespace,
558-
}, deploy)
553+
registryDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.RegistryFeastType)
559554
Expect(err).NotTo(HaveOccurred())
560-
registryContainer := services.GetRegistryContainer(*deploy)
555+
registryContainer := services.GetRegistryContainer(*registryDeploy)
561556
Expect(registryContainer.Env).To(HaveLen(1))
562557
env := getFeatureStoreYamlEnvVar(registryContainer.Env)
563558
Expect(env).NotTo(BeNil())
@@ -601,7 +596,9 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
601596
}
602597
Expect(repoConfig).To(Equal(testConfig))
603598

604-
offlineContainer := services.GetOfflineContainer(*deploy)
599+
offlineDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.OfflineFeastType)
600+
Expect(err).NotTo(HaveOccurred())
601+
offlineContainer := services.GetOfflineContainer(*offlineDeploy)
605602
Expect(offlineContainer.Env).To(HaveLen(1))
606603
assertEnvFrom(*offlineContainer)
607604
env = getFeatureStoreYamlEnvVar(offlineContainer.Env)
@@ -618,7 +615,9 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
618615
Expect(err).NotTo(HaveOccurred())
619616
Expect(repoConfigOffline).To(Equal(testConfig))
620617

621-
onlineContainer := services.GetOnlineContainer(*deploy)
618+
onlineDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.OnlineFeastType)
619+
Expect(err).NotTo(HaveOccurred())
620+
onlineContainer := services.GetOnlineContainer(*onlineDeploy)
622621
Expect(onlineContainer.VolumeMounts).To(HaveLen(1))
623622
Expect(onlineContainer.Env).To(HaveLen(1))
624623
assertEnvFrom(*onlineContainer)
@@ -636,7 +635,7 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
636635
err = yaml.Unmarshal(envByte, repoConfigOnline)
637636
Expect(err).NotTo(HaveOccurred())
638637
Expect(repoConfigOnline).To(Equal(testConfig))
639-
onlineContainer = services.GetOnlineContainer(*deploy)
638+
onlineContainer = services.GetOnlineContainer(*onlineDeploy)
640639
Expect(onlineContainer.Env).To(HaveLen(1))
641640

642641
// check client config
@@ -694,12 +693,9 @@ var _ = Describe("FeatureStore Controller - db storage services", func() {
694693
feast.Handler.FeatureStore = resource
695694

696695
// check online config
697-
err = k8sClient.Get(ctx, types.NamespacedName{
698-
Name: objMeta.Name,
699-
Namespace: objMeta.Namespace,
700-
}, deploy)
696+
onlineDeploy, err = getDeploymentByType(ctx, k8sClient, resource, services.OnlineFeastType)
701697
Expect(err).NotTo(HaveOccurred())
702-
onlineContainer = services.GetOnlineContainer(*deploy)
698+
onlineContainer = services.GetOnlineContainer(*onlineDeploy)
703699
env = getFeatureStoreYamlEnvVar(onlineContainer.Env)
704700
Expect(env).NotTo(BeNil())
705701

infra/feast-operator/internal/controller/featurestore_controller_ephemeral_test.go

Lines changed: 73 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -198,17 +198,30 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
198198

199199
Expect(resource.Status.Phase).To(Equal(feastdevv1.PendingPhase))
200200

201-
// check deployment
202-
deploy := &appsv1.Deployment{}
203-
objMeta := feast.GetObjectMeta()
204-
err = k8sClient.Get(ctx, types.NamespacedName{
205-
Name: objMeta.Name,
206-
Namespace: objMeta.Namespace,
207-
}, deploy)
201+
// check deployments per service
202+
registryDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.RegistryFeastType)
203+
Expect(err).NotTo(HaveOccurred())
204+
Expect(registryDeploy.Spec.Replicas).To(Equal(int32Ptr(1)))
205+
Expect(controllerutil.HasControllerReference(registryDeploy)).To(BeTrue())
206+
Expect(registryDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
207+
208+
offlineDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.OfflineFeastType)
209+
Expect(err).NotTo(HaveOccurred())
210+
Expect(offlineDeploy.Spec.Replicas).To(Equal(int32Ptr(1)))
211+
Expect(controllerutil.HasControllerReference(offlineDeploy)).To(BeTrue())
212+
Expect(offlineDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
213+
214+
onlineDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.OnlineFeastType)
215+
Expect(err).NotTo(HaveOccurred())
216+
Expect(onlineDeploy.Spec.Replicas).To(Equal(int32Ptr(1)))
217+
Expect(controllerutil.HasControllerReference(onlineDeploy)).To(BeTrue())
218+
Expect(onlineDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
219+
220+
uiDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.UIFeastType)
208221
Expect(err).NotTo(HaveOccurred())
209-
Expect(deploy.Spec.Replicas).To(Equal(int32Ptr(1)))
210-
Expect(controllerutil.HasControllerReference(deploy)).To(BeTrue())
211-
Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(4))
222+
Expect(uiDeploy.Spec.Replicas).To(Equal(int32Ptr(1)))
223+
Expect(controllerutil.HasControllerReference(uiDeploy)).To(BeTrue())
224+
Expect(uiDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
212225
svc := &corev1.Service{}
213226
err = k8sClient.Get(ctx, types.NamespacedName{
214227
Name: feast.GetFeastServiceName(services.RegistryFeastType),
@@ -243,7 +256,7 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
243256
deployList := appsv1.DeploymentList{}
244257
err = k8sClient.List(ctx, &deployList, listOpts)
245258
Expect(err).NotTo(HaveOccurred())
246-
Expect(deployList.Items).To(HaveLen(1))
259+
Expect(deployList.Items).To(HaveLen(4))
247260

248261
svcList := corev1.ServiceList{}
249262
err = k8sClient.List(ctx, &svcList, listOpts)
@@ -264,16 +277,11 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
264277
},
265278
}
266279

267-
// check deployment
268-
deploy := &appsv1.Deployment{}
269-
objMeta := feast.GetObjectMeta()
270-
err = k8sClient.Get(ctx, types.NamespacedName{
271-
Name: objMeta.Name,
272-
Namespace: objMeta.Namespace,
273-
}, deploy)
280+
// check deployments per service
281+
registryDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.RegistryFeastType)
274282
Expect(err).NotTo(HaveOccurred())
275-
Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(4))
276-
registryContainer := services.GetRegistryContainer(*deploy)
283+
Expect(registryDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
284+
registryContainer := services.GetRegistryContainer(*registryDeploy)
277285
Expect(registryContainer.Env).To(HaveLen(1))
278286
env := getFeatureStoreYamlEnvVar(registryContainer.Env)
279287
Expect(env).NotTo(BeNil())
@@ -306,7 +314,10 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
306314
}
307315
Expect(repoConfig).To(Equal(testConfig))
308316

309-
offlineContainer := services.GetOfflineContainer(*deploy)
317+
offlineDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.OfflineFeastType)
318+
Expect(err).NotTo(HaveOccurred())
319+
Expect(offlineDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
320+
offlineContainer := services.GetOfflineContainer(*offlineDeploy)
310321
Expect(offlineContainer.Env).To(HaveLen(1))
311322
assertEnvFrom(*offlineContainer)
312323
env = getFeatureStoreYamlEnvVar(offlineContainer.Env)
@@ -326,7 +337,10 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
326337
Expect(err).NotTo(HaveOccurred())
327338
Expect(repoConfigOffline).To(Equal(testConfig))
328339

329-
onlineContainer := services.GetOnlineContainer(*deploy)
340+
onlineDeploy, err := getDeploymentByType(ctx, k8sClient, resource, services.OnlineFeastType)
341+
Expect(err).NotTo(HaveOccurred())
342+
Expect(onlineDeploy.Spec.Template.Spec.Containers).To(HaveLen(1))
343+
onlineContainer := services.GetOnlineContainer(*onlineDeploy)
330344
Expect(onlineContainer.Env).To(HaveLen(3))
331345
Expect(onlineContainer.ImagePullPolicy).To(Equal(corev1.PullAlways))
332346
env = getFeatureStoreYamlEnvVar(onlineContainer.Env)
@@ -395,12 +409,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
395409
feast.Handler.FeatureStore = resource
396410

397411
// check registry
398-
err = k8sClient.Get(ctx, types.NamespacedName{
399-
Name: objMeta.Name,
400-
Namespace: objMeta.Namespace,
401-
}, deploy)
412+
registryDeploy, err = getDeploymentByType(ctx, k8sClient, resource, services.RegistryFeastType)
402413
Expect(err).NotTo(HaveOccurred())
403-
registryContainer = services.GetRegistryContainer(*deploy)
414+
registryContainer = services.GetRegistryContainer(*registryDeploy)
404415
env = getFeatureStoreYamlEnvVar(registryContainer.Env)
405416
Expect(env).NotTo(BeNil())
406417
fsYamlStr, err = feast.GetServiceFeatureStoreYamlBase64()
@@ -417,7 +428,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
417428
Expect(repoConfig).To(Equal(testConfig))
418429

419430
// check offline config
420-
offlineContainer = services.GetRegistryContainer(*deploy)
431+
offlineDeploy, err = getDeploymentByType(ctx, k8sClient, resource, services.OfflineFeastType)
432+
Expect(err).NotTo(HaveOccurred())
433+
offlineContainer = services.GetOfflineContainer(*offlineDeploy)
421434
env = getFeatureStoreYamlEnvVar(offlineContainer.Env)
422435
Expect(env).NotTo(BeNil())
423436

@@ -433,7 +446,9 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
433446
Expect(repoConfigOffline).To(Equal(testConfig))
434447

435448
// check online config
436-
onlineContainer = services.GetOnlineContainer(*deploy)
449+
onlineDeploy, err = getDeploymentByType(ctx, k8sClient, resource, services.OnlineFeastType)
450+
Expect(err).NotTo(HaveOccurred())
451+
onlineContainer = services.GetOnlineContainer(*onlineDeploy)
437452
env = getFeatureStoreYamlEnvVar(onlineContainer.Env)
438453
Expect(env).NotTo(BeNil())
439454

@@ -456,3 +471,32 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
456471
})
457472
})
458473
})
474+
475+
func getDeploymentByType(ctx context.Context, c client.Client, resource *feastdevv1.FeatureStore, feastType services.FeastServiceType) (*appsv1.Deployment, error) {
476+
deploy := &appsv1.Deployment{}
477+
name := services.GetFeastServiceName(resource, feastType)
478+
if err := c.Get(ctx, types.NamespacedName{Name: name, Namespace: resource.Namespace}, deploy); err == nil {
479+
return deploy, nil
480+
} else if !errors.IsNotFound(err) {
481+
return nil, err
482+
}
483+
484+
reqName, err := labels.NewRequirement(services.NameLabelKey, selection.Equals, []string{resource.Name})
485+
if err != nil {
486+
return nil, err
487+
}
488+
reqType, err := labels.NewRequirement(services.ServiceTypeLabelKey, selection.Equals, []string{string(feastType)})
489+
if err != nil {
490+
return nil, err
491+
}
492+
selector := labels.NewSelector().Add(*reqName, *reqType)
493+
listOpts := &client.ListOptions{Namespace: resource.Namespace, LabelSelector: selector}
494+
deployList := appsv1.DeploymentList{}
495+
if err := c.List(ctx, &deployList, listOpts); err != nil {
496+
return nil, err
497+
}
498+
if len(deployList.Items) == 0 {
499+
return nil, fmt.Errorf("deployment for %s not found", feastType)
500+
}
501+
return &deployList.Items[0], nil
502+
}

0 commit comments

Comments
 (0)