Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: PVC configuration and impl #4750

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

dmartinol
Copy link
Contributor

@dmartinol dmartinol commented Nov 8, 2024

What this PR does / why we need it:

Adds durable persistence configuration to Feast services deployed with the operator.

PVC configuration allows to either create a new PVC or use an existing one:

apiVersion: feast.dev/v1alpha1
kind: FeatureStore
metadata:
  name: sample-pvc-persistence
spec:
  feastProject: my_project
  services:
    onlineStore:
      persistence:
        file:
          path: online_store.db
          pvc:
            ref:
              name: online-pvc
            mountPath: /data/online
    offlineStore:
      persistence:
        file:
          type: duckdb
          pvc:
            create:
              storageClassName: standard
              resources:
                requests:
                  storage: 5Gi
            mountPath: /data/offline
    registry:
      local:
        persistence:
          file:
            path: registry.db
            pvc:
              create: {}
              mountPath: /data/registry

Which issue(s) this PR fixes:

Relates to #4561

Signed-off-by: Daniele Martinoli <[email protected]>
Signed-off-by: Daniele Martinoli <[email protected]>
@dmartinol dmartinol requested a review from a team as a code owner November 8, 2024 17:26
@dmartinol
Copy link
Contributor Author

@tchughesiv @tmihalac I also splitted the controller tests in multiple files to simplify them

Comment on lines 166 to 171
err := feast.setPVC(pvc, pvcCreate, feastType)
if err != nil {
return err
}
if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, pvc, controllerutil.MutateFn(func() error {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
err := feast.setPVC(pvc, pvcCreate, feastType)
if err != nil {
return err
}
if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, pvc, controllerutil.MutateFn(func() error {
return nil
if op, err := controllerutil.CreateOrUpdate(feast.Context, feast.Client, pvc, controllerutil.MutateFn(func() error {
return feast.setPVC(pvc, pvcCreate, feastType)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the PVC object mods need to happen within the CreateOrUpdate func ()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was this moved because of the immutable nature of the PVC?

Copy link
Contributor

@tchughesiv tchughesiv Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if so, we could be explicit with PVC creation and use feast.Client.Create() here instead? it could be used alongside if !apierrors.IsAlreadyExists(err) {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried the Create option but creation was not an issue. Deletion seems to fail, at least in the tests (just restored them to see the failure)

Signed-off-by: Daniele Martinoli <[email protected]>
Signed-off-by: Daniele Martinoli <[email protected]>
Signed-off-by: Daniele Martinoli <[email protected]>
Comment on lines 127 to +130
type RegistryFilePersistence struct {
Path string `json:"path,omitempty"`
Path string `json:"path,omitempty"`
PvcConfig *PvcConfig `json:"pvc,omitempty"`
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnlineStoreFilePersistence and RegistryFilePersistence are identical. Any reason we shouldn't make a single FilePersistence struct instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registry also supports the s3_additional_kwargs options (which will go under the Registry.Persistence.File section). Will we add it to a new PR dedicated to object store settings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so this is in prep for future work... got it, thanks

Signed-off-by: Daniele Martinoli <[email protected]>
Signed-off-by: Daniele Martinoli <[email protected]>
Copy link
Contributor

@tchughesiv tchughesiv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@dmartinol
Copy link
Contributor Author

@lokeshrangineni @redhatHameed can you take a look?

@lokeshrangineni lokeshrangineni enabled auto-merge (squash) November 12, 2024 15:20
Copy link
Contributor

@redhatHameed redhatHameed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks lgtm some minor comments.

// Offline server has an `offline_store` section and a remote `registry`
if feastType == OfflineFeastType && appliedSpec.Services.OfflineStore != nil {
if feastType == OfflineFeastType && services.OfflineStore != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not an issue, how about using switch statement here

switch feastType {
	case OfflineFeastType:
		if services.OfflineStore != nil {

s3_additional_kwargs:
ServerSideEncryption: AES256
ACL: bucket-owner-full-control
CacheControl: max-age=3600
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add new line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file should not be in the PR, will remove it

path: registry.db
pvc:
create: {}
mountPath: /data/registry
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why create section empty here, is there related docs added for this configuration. and looks the settings not consistent with each type

For example registry

          file:
            path: registry.db
            pvc:
              create: {}
              mountPath: /data/registry

online

          path: online_store.db
          pvc:
            ref:
              name: online-pvc
            mountPath: /data/online

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think he's demonstrating 3 different types of pvc configurations. all 3 feast service types can be configured similarly. an empty create: tells the operator that the user wants a pvc, but doesn't want to define it. the operator will apply defaults during the creation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no doc apart from the CRD definition, which has some comments on it.
wrt your questions:

  • pvc can have either create or ref, and create has default fields in case they are omitted
    Anyway, to avoid misunderstandings, I will add the other settings too and avoid defaults

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo, CRD should be the doc of choice for API. @dmartinol i like the file in its current state, shows the user all their options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, reverted previous change

func hasPvcConfig(featureStore *feastdevv1alpha1.FeatureStore, feastType FeastServiceType) (*feastdevv1alpha1.PvcConfig, bool) {
services := featureStore.Status.Applied.Services
var pvcConfig *feastdevv1alpha1.PvcConfig = nil
if feastType == OnlineFeastType && services.OnlineStore != nil && services.OnlineStore.Persistence.FilePersistence != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not an issue, how about considering switch statment here

switch feastType {
 case OnlineFeastType:
     if services.OnlineStore != nil {  

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tchughesiv ? I also thought of using the switch but I wanted to be consistent with the existing code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think that would be a better approach, so def go for it

Comment on lines 255 to 264
func (feast *FeastServices) createNewPVC(pvcCreate *feastdevv1alpha1.PvcCreate, feastType FeastServiceType) (*corev1.PersistentVolumeClaim, error) {
pvc := feast.initPVC(feastType)

pvc.Spec = corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteMany},
Resources: pvcCreate.Resources,
}
return pvc, controllerutil.SetControllerReference(feast.FeatureStore, pvc, feast.Scheme)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is StorageClassName set elsewhere? i can't seem to find where its being set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. will add a test for it

Signed-off-by: Daniele Martinoli <[email protected]>
Signed-off-by: Daniele Martinoli <[email protected]>
@@ -67,6 +67,8 @@ var _ = Describe("FeatureStore Controller-Ephemeral services", func() {
onlineStoreMountPath := "/online"
registryMountPath := "/registry"

storageClassName := "default"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a bit confusing to use "default" as the storageClassName here, since you're testing a scenario where a user actually defines the storage class name... as opposed to leaving it blank and using the default. maybe use a different name? "test" ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants