diff --git a/core/src/main/java/feast/core/grpc/CoreServiceImpl.java b/core/src/main/java/feast/core/grpc/CoreServiceImpl.java index 0c7ea5828e7..412d3d31198 100644 --- a/core/src/main/java/feast/core/grpc/CoreServiceImpl.java +++ b/core/src/main/java/feast/core/grpc/CoreServiceImpl.java @@ -183,6 +183,17 @@ public void archiveProject( accessManagementService.archiveProject(request.getName()); responseObserver.onNext(ArchiveProjectResponse.getDefaultInstance()); responseObserver.onCompleted(); + } catch (IllegalArgumentException e) { + log.error("Recieved an invalid request on calling archiveProject method:", e); + responseObserver.onError( + Status.INVALID_ARGUMENT + .withDescription(e.getMessage()) + .withCause(e) + .asRuntimeException()); + } catch (UnsupportedOperationException e) { + log.error("Attempted to archive an unsupported project:", e); + responseObserver.onError( + Status.UNIMPLEMENTED.withDescription(e.getMessage()).withCause(e).asRuntimeException()); } catch (Exception e) { log.error("Exception has occurred in the createProject method: ", e); responseObserver.onError( diff --git a/core/src/main/java/feast/core/model/FeatureSet.java b/core/src/main/java/feast/core/model/FeatureSet.java index 3008c37a3f9..f7b2dc7cd49 100644 --- a/core/src/main/java/feast/core/model/FeatureSet.java +++ b/core/src/main/java/feast/core/model/FeatureSet.java @@ -39,7 +39,6 @@ uniqueConstraints = @UniqueConstraint(columnNames = {"name", "project_name"})) public class FeatureSet extends AbstractTimestampEntity { - // Id of the featureSet, defined as project/feature_set_name:feature_set_version @Id @GeneratedValue private long id; // Name of the featureSet diff --git a/core/src/main/java/feast/core/model/Project.java b/core/src/main/java/feast/core/model/Project.java index d6e6149394b..c55830c8248 100644 --- a/core/src/main/java/feast/core/model/Project.java +++ b/core/src/main/java/feast/core/model/Project.java @@ -34,6 +34,7 @@ @Entity @Table(name = "projects") public class Project { + public static final String DEFAULT_NAME = "default"; // Name of the project @Id diff --git a/core/src/main/java/feast/core/service/AccessManagementService.java b/core/src/main/java/feast/core/service/AccessManagementService.java index df92750e94f..5b02d6f3c4a 100644 --- a/core/src/main/java/feast/core/service/AccessManagementService.java +++ b/core/src/main/java/feast/core/service/AccessManagementService.java @@ -28,12 +28,15 @@ @Slf4j @Service public class AccessManagementService { - private ProjectRepository projectRepository; @Autowired public AccessManagementService(ProjectRepository projectRepository) { this.projectRepository = projectRepository; + // create default project if it does not yet exist. + if (!projectRepository.existsById(Project.DEFAULT_NAME)) { + this.createProject(Project.DEFAULT_NAME); + } } /** @@ -61,6 +64,9 @@ public void archiveProject(String name) { if (!project.isPresent()) { throw new IllegalArgumentException(String.format("Could not find project: \"%s\"", name)); } + if (name.equals(Project.DEFAULT_NAME)) { + throw new UnsupportedOperationException("Archiving the default project is not allowed."); + } Project p = project.get(); p.setArchived(true); projectRepository.saveAndFlush(p); diff --git a/core/src/main/java/feast/core/service/SpecService.java b/core/src/main/java/feast/core/service/SpecService.java index 767b3fe6920..01cd264c761 100644 --- a/core/src/main/java/feast/core/service/SpecService.java +++ b/core/src/main/java/feast/core/service/SpecService.java @@ -80,7 +80,8 @@ public SpecService( /** * Get a feature set matching the feature name and version and project. The feature set name and * project are required, but version can be omitted by providing 0 for its value. If the version - * is omitted, the latest feature set will be provided. + * is omitted, the latest feature set will be provided. If the project is omitted, the default + * would be used. * * @param request: GetFeatureSetRequest Request containing filter parameters. * @return Returns a GetFeatureSetResponse containing a feature set.. @@ -94,8 +95,9 @@ public GetFeatureSetResponse getFeatureSet(GetFeatureSetRequest request) if (request.getName().isEmpty()) { throw new IllegalArgumentException("No feature set name provided"); } + // Autofill default project if project is not specified if (request.getProject().isEmpty()) { - throw new IllegalArgumentException("No project provided"); + request = request.toBuilder().setProject(Project.DEFAULT_NAME).build(); } FeatureSet featureSet; @@ -117,7 +119,8 @@ public GetFeatureSetResponse getFeatureSet(GetFeatureSetRequest request) * projects. * *

Project name can be explicitly provided, or an asterisk can be provided to match all - * projects. It is not possible to provide a combination of asterisks/wildcards and text. + * projects. It is not possible to provide a combination of asterisks/wildcards and text. If the + * project name is omitted, the default project would be used. * *

The feature set name in the filter accepts an asterisk as a wildcard. All matching feature * sets will be returned. Regex is not supported. Explicitly defining a feature set name is not @@ -131,14 +134,19 @@ public ListFeatureSetsResponse listFeatureSets(ListFeatureSetsRequest.Filter fil String name = filter.getFeatureSetName(); String project = filter.getProject(); - if (project.isEmpty() || name.isEmpty()) { + if (name.isEmpty()) { throw new IllegalArgumentException( - "Invalid listFeatureSetRequest, missing arguments. Must provide project and feature set name."); + "Invalid listFeatureSetRequest, missing arguments. Must provide feature set name:"); } checkValidCharactersAllowAsterisk(name, "featureSetName"); checkValidCharactersAllowAsterisk(project, "projectName"); + // Autofill default project if project not specified + if (project.isEmpty()) { + project = Project.DEFAULT_NAME; + } + List featureSets = new ArrayList() {}; if (project.contains("*")) { @@ -227,12 +235,21 @@ public ListStoresResponse listStores(ListStoresRequest.Filter filter) { * *

This function is idempotent. If no changes are detected in the incoming featureSet's schema, * this method will update the incoming featureSet spec with the latest version stored in the - * repository, and return that. + * repository, and return that. If project is not specified in the given featureSet, will assign + * the featureSet to the'default' project. * * @param newFeatureSet Feature set that will be created or updated. */ public ApplyFeatureSetResponse applyFeatureSet(FeatureSetProto.FeatureSet newFeatureSet) throws InvalidProtocolBufferException { + // Autofill default project if not specified + if (newFeatureSet.getSpec().getProject().isEmpty()) { + newFeatureSet = + newFeatureSet + .toBuilder() + .setSpec(newFeatureSet.getSpec().toBuilder().setProject(Project.DEFAULT_NAME).build()) + .build(); + } // Validate incoming feature set FeatureSetValidator.validateSpec(newFeatureSet); diff --git a/core/src/test/java/feast/core/job/JobUpdateTaskTest.java b/core/src/test/java/feast/core/job/JobUpdateTaskTest.java index 3225dcd76e5..d1826738019 100644 --- a/core/src/test/java/feast/core/job/JobUpdateTaskTest.java +++ b/core/src/test/java/feast/core/job/JobUpdateTaskTest.java @@ -53,8 +53,7 @@ public class JobUpdateTaskTest { private static final FeatureSetProto.FeatureSet.Builder fsBuilder = FeatureSetProto.FeatureSet.newBuilder().setMeta(FeatureSetMeta.newBuilder()); - private static final FeatureSetSpec.Builder specBuilder = - FeatureSetSpec.newBuilder().setProject("project1"); + private static final FeatureSetSpec.Builder specBuilder = FeatureSetSpec.newBuilder(); @Mock private JobManager jobManager; diff --git a/core/src/test/java/feast/core/service/AccessManagementServiceTest.java b/core/src/test/java/feast/core/service/AccessManagementServiceTest.java new file mode 100644 index 00000000000..15be203709f --- /dev/null +++ b/core/src/test/java/feast/core/service/AccessManagementServiceTest.java @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright 2018-2019 The Feast Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package feast.core.service; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import feast.core.dao.ProjectRepository; +import feast.core.model.Project; +import java.util.Optional; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; + +public class AccessManagementServiceTest { + @Rule public ExpectedException expectedException = ExpectedException.none(); + // mocks + @Mock private ProjectRepository projectRepository; + // dummy models + private Project defaultProject; + private Project testProject; + + // test target + private AccessManagementService accessService; + + @Before + public void setup() { + initMocks(this); + // setup dummy models for testing + this.defaultProject = new Project(Project.DEFAULT_NAME); + this.testProject = new Project("project"); + // setup test target + when(this.projectRepository.existsById(Project.DEFAULT_NAME)).thenReturn(false); + this.accessService = new AccessManagementService(this.projectRepository); + } + + @Test + public void testDefaultProjectCreateInConstructor() { + verify(this.projectRepository).saveAndFlush(this.defaultProject); + } + + @Test + public void testArchiveProject() { + when(this.projectRepository.findById("project")).thenReturn(Optional.of(this.testProject)); + this.accessService.archiveProject("project"); + this.testProject.setArchived(true); + verify(this.projectRepository).saveAndFlush(this.testProject); + // reset archived flag + this.testProject.setArchived(false); + } + + @Test + public void shouldNotArchiveDefaultProject() { + expectedException.expect(IllegalArgumentException.class); + this.accessService.archiveProject(Project.DEFAULT_NAME); + } +} diff --git a/core/src/test/java/feast/core/service/JobServiceTest.java b/core/src/test/java/feast/core/service/JobServiceTest.java index 87b32d1a208..ff056287f9b 100644 --- a/core/src/test/java/feast/core/service/JobServiceTest.java +++ b/core/src/test/java/feast/core/service/JobServiceTest.java @@ -69,7 +69,6 @@ public class JobServiceTest { // test target public JobService jobService; - /* unit test setup */ @Before public void setup() { initMocks(this); @@ -107,7 +106,6 @@ public void setup() { new JobService(this.jobRepository, this.specService, Arrays.asList(this.jobManager)); } - // setup fake spec service public void setupSpecService() { try { ListFeatureSetsResponse response = @@ -124,7 +122,6 @@ public void setupSpecService() { } } - // setup fake job repository public void setupJobRepository() { when(this.jobRepository.findById(this.job.getId())).thenReturn(Optional.of(this.job)); when(this.jobRepository.findByStoreName(this.dataStore.getName())) @@ -134,14 +131,12 @@ public void setupJobRepository() { when(this.jobRepository.findAll()).thenReturn(Arrays.asList(this.job)); } - // TODO: setup fake job manager public void setupJobManager() { when(this.jobManager.getRunnerType()).thenReturn(Runner.DATAFLOW); when(this.jobManager.restartJob(this.job)) .thenReturn(this.newDummyJob(this.job.getId(), this.job.getExtId(), JobStatus.PENDING)); } - // dummy model constructorss private FeatureSet newDummyFeatureSet(String name, int version, String project) { Feature feature = TestObjectFactory.CreateFeature(name + "_feature", Enum.INT64); Entity entity = TestObjectFactory.CreateEntity(name + "_entity", Enum.STRING); @@ -203,7 +198,6 @@ private List newDummyListRequestFilters() { .build()); } - /* unit tests */ private ListIngestionJobsResponse tryListJobs(ListIngestionJobsRequest request) { ListIngestionJobsResponse response = null; try { @@ -216,7 +210,6 @@ private ListIngestionJobsResponse tryListJobs(ListIngestionJobsRequest request) return response; } - // list jobs @Test public void testListJobsById() { ListIngestionJobsRequest.Filter filter = @@ -275,7 +268,6 @@ public void testListIngestionJobByFeatureSetReference() { assertThat(this.tryListJobs(request).getJobs(0), equalTo(this.ingestionJob)); } - // stop jobs private StopIngestionJobResponse tryStopJob( StopIngestionJobRequest request, boolean expectError) { StopIngestionJobResponse response = null; diff --git a/core/src/test/java/feast/core/service/SpecServiceTest.java b/core/src/test/java/feast/core/service/SpecServiceTest.java index 5a9c7c161f3..e584ee71e00 100644 --- a/core/src/test/java/feast/core/service/SpecServiceTest.java +++ b/core/src/test/java/feast/core/service/SpecServiceTest.java @@ -1,6 +1,6 @@ /* * SPDX-License-Identifier: Apache-2.0 - * Copyright 2018-2019 The Feast Authors + * Copyright 2018-2020 The Feast Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import feast.proto.core.CoreServiceProto.ApplyFeatureSetResponse; import feast.proto.core.CoreServiceProto.ApplyFeatureSetResponse.Status; import feast.proto.core.CoreServiceProto.GetFeatureSetRequest; +import feast.proto.core.CoreServiceProto.GetFeatureSetResponse; import feast.proto.core.CoreServiceProto.ListFeatureSetsRequest.Filter; import feast.proto.core.CoreServiceProto.ListFeatureSetsResponse; import feast.proto.core.CoreServiceProto.ListStoresRequest; @@ -105,11 +106,13 @@ public void setUp() { Feature f3f1 = TestObjectFactory.CreateFeature("f3f1", Enum.INT64); Feature f3f2 = TestObjectFactory.CreateFeature("f3f2", Enum.INT64); Entity f3e1 = TestObjectFactory.CreateEntity("f3e1", Enum.STRING); - FeatureSet featureSet3v1 = + FeatureSet featureSet3 = TestObjectFactory.CreateFeatureSet( "f3", "project1", Arrays.asList(f3e1), Arrays.asList(f3f2, f3f1)); - featureSets = Arrays.asList(featureSet1, featureSet2); + FeatureSet featureSet4 = newDummyFeatureSet("f4", Project.DEFAULT_NAME); + featureSets = Arrays.asList(featureSet1, featureSet2, featureSet3, featureSet4); + when(featureSetRepository.findAll()).thenReturn(featureSets); when(featureSetRepository.findAllByOrderByNameAsc()).thenReturn(featureSets); when(featureSetRepository.findFeatureSetByNameAndProject_Name("f1", "project1")) @@ -160,15 +163,6 @@ public void shouldGetAllFeatureSetsIfOnlyWildcardsProvided() assertThat(actual, equalTo(expected)); } - @Test - public void listFeatureSetShouldFailIfFeatureSetProvidedWithoutProject() - throws InvalidProtocolBufferException { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage( - "Invalid listFeatureSetRequest, missing arguments. Must provide project and feature set name."); - specService.listFeatureSets(Filter.newBuilder().setFeatureSetName("f1").build()); - } - @Test public void shouldGetAllFeatureSetsMatchingNameWithWildcardSearch() throws InvalidProtocolBufferException { @@ -511,6 +505,26 @@ public void applyFeatureSetShouldCreateProjectWhenNotAlreadyExists() equalTo(incomingFeatureSet.getSpec().getProject())); } + @Test + public void applyFeatureSetShouldUsedDefaultProjectIfUnspecified() + throws InvalidProtocolBufferException { + Feature f3f1 = TestObjectFactory.CreateFeature("f3f1", Enum.INT64); + Feature f3f2 = TestObjectFactory.CreateFeature("f3f2", Enum.INT64); + Entity f3e1 = TestObjectFactory.CreateEntity("f3e1", Enum.STRING); + + // In protov3, unspecified project defaults to "" + FeatureSetProto.FeatureSet incomingFeatureSet = + TestObjectFactory.CreateFeatureSet("f3", "", Arrays.asList(f3e1), Arrays.asList(f3f2, f3f1)) + .toProto(); + ApplyFeatureSetResponse applyFeatureSetResponse = + specService.applyFeatureSet(incomingFeatureSet); + assertThat(applyFeatureSetResponse.getStatus(), equalTo(Status.CREATED)); + + assertThat( + applyFeatureSetResponse.getFeatureSet().getSpec().getProject(), + equalTo(Project.DEFAULT_NAME)); + } + @Test public void applyFeatureSetShouldFailWhenProjectIsArchived() throws InvalidProtocolBufferException { @@ -661,10 +675,20 @@ public void shouldDoNothingIfNoChange() throws InvalidProtocolBufferException { } @Test - public void shouldFailIfGetFeatureSetWithoutProject() throws InvalidProtocolBufferException { - expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("No project provided"); - specService.getFeatureSet(GetFeatureSetRequest.newBuilder().setName("f1").build()); + public void getOrListFeatureSetShouldUseDefaultProjectIfProjectUnspecified() + throws InvalidProtocolBufferException { + when(featureSetRepository.findFeatureSetByNameAndProject_Name("f4", Project.DEFAULT_NAME)) + .thenReturn(featureSets.get(3)); + FeatureSet expected = featureSets.get(3); + // check getFeatureSet() + GetFeatureSetResponse getResponse = + specService.getFeatureSet(GetFeatureSetRequest.newBuilder().setName("f4").build()); + assertThat(getResponse.getFeatureSet(), equalTo(expected.toProto())); + + // check listFeatureSets() + ListFeatureSetsResponse listResponse = + specService.listFeatureSets(Filter.newBuilder().setFeatureSetName("f4").build()); + assertThat(listResponse.getFeatureSetsList(), equalTo(Arrays.asList(expected.toProto()))); } private FeatureSet newDummyFeatureSet(String name, String project) { diff --git a/protos/feast/core/CoreService.proto b/protos/feast/core/CoreService.proto index e81a260731b..3cd3c756830 100644 --- a/protos/feast/core/CoreService.proto +++ b/protos/feast/core/CoreService.proto @@ -65,8 +65,8 @@ service CoreService { rpc UpdateStore (UpdateStoreRequest) returns (UpdateStoreResponse); // Creates a project. Projects serve as namespaces within which resources like features will be - // created. Both feature set names as well as field names must be unique within a project. Project - // names themselves must be globally unique. + // created. Feature set names as must be unique within a project while field (Feature/Entity) names + // must be unique within a Feature Set. Project names themselves must be globally unique. rpc CreateProject (CreateProjectRequest) returns (CreateProjectResponse); // Archives a project. Archived projects will continue to exist and function, but won't be visible @@ -99,7 +99,7 @@ service CoreService { // Request for a single feature set message GetFeatureSetRequest { - // Name of project the feature set belongs to (required) + // Name of project the feature set belongs to. If omitted will default to 'default' project. string project = 3; // Name of feature set (required). @@ -122,6 +122,7 @@ message ListFeatureSetsRequest { // If an asterisk is provided, filtering on projects will be disabled. All projects will // be matched. It is NOT possible to provide an asterisk with a string in order to do // pattern matching. + // If unspecified this field will default to the default project 'default'. string project = 3; // Name of the desired feature set. Asterisks can be used as wildcards in the name. @@ -153,6 +154,9 @@ message ListStoresResponse { } message ApplyFeatureSetRequest { + // Feature set version + // If project is unspecified, will default to 'default' project. + // If project specified does not exist, the project would be automatically created. feast.core.FeatureSet feature_set = 1; } diff --git a/protos/feast/serving/ServingService.proto b/protos/feast/serving/ServingService.proto index edef4a4a228..cd7d51bd59c 100644 --- a/protos/feast/serving/ServingService.proto +++ b/protos/feast/serving/ServingService.proto @@ -19,7 +19,6 @@ syntax = "proto3"; package feast.serving; import "google/protobuf/timestamp.proto"; -import "google/protobuf/duration.proto"; import "feast/types/Value.proto"; option java_package = "feast.proto.serving"; @@ -63,18 +62,19 @@ message GetFeastServingInfoResponse { } message FeatureReference { - // Project name + // Project name. This field is optional, if unspecified will default to 'default'. string project = 1; // Feature name string name = 2; - // The features will be retrieved if: - // entity_timestamp - max_age <= event_timestamp <= entity_timestamp - // - // If unspecified the default max_age specified in FeatureSetSpec will - // be used. - google.protobuf.Duration max_age = 4; + // Feature set name specifying the feature set of this referenced feature. + // This field is optional if the feature referenced is unique across the project + // in which case the feature set would be automatically infered + string feature_set = 5; + + // Feature version and max_age was removed in v0.5.0 + reserved 3, 4; } message GetOnlineFeaturesRequest { diff --git a/sdk/go/client.go b/sdk/go/client.go index edb135e4655..38c9e2fb7e5 100644 --- a/sdk/go/client.go +++ b/sdk/go/client.go @@ -6,6 +6,7 @@ import ( "github.com/opentracing/opentracing-go" "github.com/feast-dev/feast/sdk/go/protos/feast/serving" + "github.com/feast-dev/feast/sdk/go/protos/feast/types" "google.golang.org/grpc" "go.opencensus.io/plugin/ocgrpc" @@ -50,6 +51,31 @@ func (fc *GrpcClient) GetOnlineFeatures(ctx context.Context, req *OnlineFeatures } resp, err := fc.cli.GetOnlineFeatures(ctx, featuresRequest) + // collect unqiue entity refs from entity rows + var entityRefs map[string]struct{} + for _, entityRows := range req.Entities { + for ref, _ := range entityRows { + entityRefs[ref] = struct{}{} + } + } + + // strip projects from to projects + for _, fieldValue := range resp.GetFieldValues() { + var stripFields map[string]*types.Value + for refStr, value := range fieldValue.Fields { + _, isEntity := entityRefs[refStr] + if !isEntity { // is feature ref + featureRef, err := parseFeatureRef(refStr, true) + if err != nil { + return nil, err + } + stripRefStr := toFeatureRefStr(featureRef) + stripFields[stripRefStr] = value + } + } + fieldValue.Fields = stripFields + } + return &OnlineFeaturesResponse{RawResponse: resp}, err } diff --git a/sdk/go/protos/feast/core/CoreService.pb.go b/sdk/go/protos/feast/core/CoreService.pb.go index 1af820f50bb..75f988ed528 100644 --- a/sdk/go/protos/feast/core/CoreService.pb.go +++ b/sdk/go/protos/feast/core/CoreService.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/CoreService.proto @@ -45,6 +45,7 @@ const ( // of the legacy proto package is being used. const _ = proto.ProtoPackageIsVersion4 +// TODO: 0 should correspond to invalid rather than NO_CHANGE type ApplyFeatureSetResponse_Status int32 const ( @@ -155,7 +156,7 @@ type GetFeatureSetRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Name of project the feature set belongs to (required) + // Name of project the feature set belongs to. If omitted will default to 'default' project. Project string `protobuf:"bytes,3,opt,name=project,proto3" json:"project,omitempty"` // Name of feature set (required). Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -449,6 +450,9 @@ type ApplyFeatureSetRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + // Feature set version + // If project is unspecified, will default to 'default' project. + // If project specified does not exist, the project would be automatically created. FeatureSet *FeatureSet `protobuf:"bytes,1,opt,name=feature_set,json=featureSet,proto3" json:"feature_set,omitempty"` } @@ -1280,6 +1284,7 @@ type ListFeatureSetsRequest_Filter struct { // If an asterisk is provided, filtering on projects will be disabled. All projects will // be matched. It is NOT possible to provide an asterisk with a string in order to do // pattern matching. + // If unspecified this field will default to the default project 'default'. Project string `protobuf:"bytes,3,opt,name=project,proto3" json:"project,omitempty"` // Name of the desired feature set. Asterisks can be used as wildcards in the name. // Matching on names is only permitted if a specific project is defined. It is disallowed @@ -1650,13 +1655,13 @@ var file_feast_core_CoreService_proto_rawDesc = []byte{ 0x70, 0x49, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x49, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x4a, - 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4f, 0x0a, 0x0a, 0x66, 0x65, - 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x10, 0x43, 0x6f, 0x72, 0x65, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, - 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, - 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x59, 0x0a, 0x10, 0x66, 0x65, + 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x10, + 0x43, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, + 0x73, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, + 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, + 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2146,7 +2151,7 @@ type CoreServiceClient interface { // All changes except the following are valid: // - Changes to feature set id (name, project) // - Changes to entities - // - Changes to feature type + // - Changes to feature name and type ApplyFeatureSet(ctx context.Context, in *ApplyFeatureSetRequest, opts ...grpc.CallOption) (*ApplyFeatureSetResponse, error) // Updates core with the configuration of the store. // @@ -2154,8 +2159,8 @@ type CoreServiceClient interface { // start or update the necessary feature population jobs for the updated store. UpdateStore(ctx context.Context, in *UpdateStoreRequest, opts ...grpc.CallOption) (*UpdateStoreResponse, error) // Creates a project. Projects serve as namespaces within which resources like features will be - // created. Both feature set names as well as field names must be unique within a project. Project - // names themselves must be globally unique. + // created. Feature set names as must be unique within a project while field (Feature/Entity) names + // must be unique within a Feature Set. Project names themselves must be globally unique. CreateProject(ctx context.Context, in *CreateProjectRequest, opts ...grpc.CallOption) (*CreateProjectResponse, error) // Archives a project. Archived projects will continue to exist and function, but won't be visible // through the Core API. Any existing ingestion or serving requests will continue to function, @@ -2322,7 +2327,7 @@ type CoreServiceServer interface { // All changes except the following are valid: // - Changes to feature set id (name, project) // - Changes to entities - // - Changes to feature type + // - Changes to feature name and type ApplyFeatureSet(context.Context, *ApplyFeatureSetRequest) (*ApplyFeatureSetResponse, error) // Updates core with the configuration of the store. // @@ -2330,8 +2335,8 @@ type CoreServiceServer interface { // start or update the necessary feature population jobs for the updated store. UpdateStore(context.Context, *UpdateStoreRequest) (*UpdateStoreResponse, error) // Creates a project. Projects serve as namespaces within which resources like features will be - // created. Both feature set names as well as field names must be unique within a project. Project - // names themselves must be globally unique. + // created. Feature set names as must be unique within a project while field (Feature/Entity) names + // must be unique within a Feature Set. Project names themselves must be globally unique. CreateProject(context.Context, *CreateProjectRequest) (*CreateProjectResponse, error) // Archives a project. Archived projects will continue to exist and function, but won't be visible // through the Core API. Any existing ingestion or serving requests will continue to function, diff --git a/sdk/go/protos/feast/core/FeatureSet.pb.go b/sdk/go/protos/feast/core/FeatureSet.pb.go index 7f072114b61..3c6441f2ac9 100644 --- a/sdk/go/protos/feast/core/FeatureSet.pb.go +++ b/sdk/go/protos/feast/core/FeatureSet.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/FeatureSet.proto @@ -325,6 +325,8 @@ type FeatureSpec struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // Value type of the feature. ValueType types.ValueType_Enum `protobuf:"varint,2,opt,name=value_type,json=valueType,proto3,enum=feast.types.ValueType_Enum" json:"value_type,omitempty"` + // Labels for user defined metadata on a feature + Labels map[string]string `protobuf:"bytes,16,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Types that are assignable to PresenceConstraints: // *FeatureSpec_Presence // *FeatureSpec_GroupPresence @@ -352,8 +354,6 @@ type FeatureSpec struct { // *FeatureSpec_TimeDomain // *FeatureSpec_TimeOfDayDomain DomainInfo isFeatureSpec_DomainInfo `protobuf_oneof:"domain_info"` - // Labels for user defined metadata on a feature - Labels map[string]string `protobuf:"bytes,19,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *FeatureSpec) Reset() { @@ -402,6 +402,13 @@ func (x *FeatureSpec) GetValueType() types.ValueType_Enum { return types.ValueType_INVALID } +func (x *FeatureSpec) GetLabels() map[string]string { + if x != nil { + return x.Labels + } + return nil +} + func (m *FeatureSpec) GetPresenceConstraints() isFeatureSpec_PresenceConstraints { if m != nil { return m.PresenceConstraints @@ -535,25 +542,18 @@ func (x *FeatureSpec) GetTimeOfDayDomain() *v0.TimeOfDayDomain { return nil } -func (x *FeatureSpec) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - type isFeatureSpec_PresenceConstraints interface { isFeatureSpec_PresenceConstraints() } type FeatureSpec_Presence struct { // Constraints on the presence of this feature in the examples. - Presence *v0.FeaturePresence `protobuf:"bytes,3,opt,name=presence,proto3,oneof"` + Presence *v0.FeaturePresence `protobuf:"bytes,30,opt,name=presence,proto3,oneof"` } type FeatureSpec_GroupPresence struct { // Only used in the context of a "group" context, e.g., inside a sequence. - GroupPresence *v0.FeaturePresenceWithinGroup `protobuf:"bytes,4,opt,name=group_presence,json=groupPresence,proto3,oneof"` + GroupPresence *v0.FeaturePresenceWithinGroup `protobuf:"bytes,31,opt,name=group_presence,json=groupPresence,proto3,oneof"` } func (*FeatureSpec_Presence) isFeatureSpec_PresenceConstraints() {} @@ -567,13 +567,13 @@ type isFeatureSpec_ShapeType interface { type FeatureSpec_Shape struct { // The feature has a fixed shape corresponding to a multi-dimensional // tensor. - Shape *v0.FixedShape `protobuf:"bytes,5,opt,name=shape,proto3,oneof"` + Shape *v0.FixedShape `protobuf:"bytes,32,opt,name=shape,proto3,oneof"` } type FeatureSpec_ValueCount struct { // The feature doesn't have a well defined shape. All we know are limits on // the minimum and maximum number of values. - ValueCount *v0.ValueCount `protobuf:"bytes,6,opt,name=value_count,json=valueCount,proto3,oneof"` + ValueCount *v0.ValueCount `protobuf:"bytes,33,opt,name=value_count,json=valueCount,proto3,oneof"` } func (*FeatureSpec_Shape) isFeatureSpec_ShapeType() {} @@ -586,53 +586,53 @@ type isFeatureSpec_DomainInfo interface { type FeatureSpec_Domain struct { // Reference to a domain defined at the schema level. - Domain string `protobuf:"bytes,7,opt,name=domain,proto3,oneof"` + Domain string `protobuf:"bytes,34,opt,name=domain,proto3,oneof"` } type FeatureSpec_IntDomain struct { // Inline definitions of domains. - IntDomain *v0.IntDomain `protobuf:"bytes,8,opt,name=int_domain,json=intDomain,proto3,oneof"` + IntDomain *v0.IntDomain `protobuf:"bytes,35,opt,name=int_domain,json=intDomain,proto3,oneof"` } type FeatureSpec_FloatDomain struct { - FloatDomain *v0.FloatDomain `protobuf:"bytes,9,opt,name=float_domain,json=floatDomain,proto3,oneof"` + FloatDomain *v0.FloatDomain `protobuf:"bytes,36,opt,name=float_domain,json=floatDomain,proto3,oneof"` } type FeatureSpec_StringDomain struct { - StringDomain *v0.StringDomain `protobuf:"bytes,10,opt,name=string_domain,json=stringDomain,proto3,oneof"` + StringDomain *v0.StringDomain `protobuf:"bytes,37,opt,name=string_domain,json=stringDomain,proto3,oneof"` } type FeatureSpec_BoolDomain struct { - BoolDomain *v0.BoolDomain `protobuf:"bytes,11,opt,name=bool_domain,json=boolDomain,proto3,oneof"` + BoolDomain *v0.BoolDomain `protobuf:"bytes,38,opt,name=bool_domain,json=boolDomain,proto3,oneof"` } type FeatureSpec_StructDomain struct { - StructDomain *v0.StructDomain `protobuf:"bytes,12,opt,name=struct_domain,json=structDomain,proto3,oneof"` + StructDomain *v0.StructDomain `protobuf:"bytes,39,opt,name=struct_domain,json=structDomain,proto3,oneof"` } type FeatureSpec_NaturalLanguageDomain struct { // Supported semantic domains. - NaturalLanguageDomain *v0.NaturalLanguageDomain `protobuf:"bytes,13,opt,name=natural_language_domain,json=naturalLanguageDomain,proto3,oneof"` + NaturalLanguageDomain *v0.NaturalLanguageDomain `protobuf:"bytes,40,opt,name=natural_language_domain,json=naturalLanguageDomain,proto3,oneof"` } type FeatureSpec_ImageDomain struct { - ImageDomain *v0.ImageDomain `protobuf:"bytes,14,opt,name=image_domain,json=imageDomain,proto3,oneof"` + ImageDomain *v0.ImageDomain `protobuf:"bytes,41,opt,name=image_domain,json=imageDomain,proto3,oneof"` } type FeatureSpec_MidDomain struct { - MidDomain *v0.MIDDomain `protobuf:"bytes,15,opt,name=mid_domain,json=midDomain,proto3,oneof"` + MidDomain *v0.MIDDomain `protobuf:"bytes,42,opt,name=mid_domain,json=midDomain,proto3,oneof"` } type FeatureSpec_UrlDomain struct { - UrlDomain *v0.URLDomain `protobuf:"bytes,16,opt,name=url_domain,json=urlDomain,proto3,oneof"` + UrlDomain *v0.URLDomain `protobuf:"bytes,43,opt,name=url_domain,json=urlDomain,proto3,oneof"` } type FeatureSpec_TimeDomain struct { - TimeDomain *v0.TimeDomain `protobuf:"bytes,17,opt,name=time_domain,json=timeDomain,proto3,oneof"` + TimeDomain *v0.TimeDomain `protobuf:"bytes,44,opt,name=time_domain,json=timeDomain,proto3,oneof"` } type FeatureSpec_TimeOfDayDomain struct { - TimeOfDayDomain *v0.TimeOfDayDomain `protobuf:"bytes,18,opt,name=time_of_day_domain,json=timeOfDayDomain,proto3,oneof"` + TimeOfDayDomain *v0.TimeOfDayDomain `protobuf:"bytes,45,opt,name=time_of_day_domain,json=timeOfDayDomain,proto3,oneof"` } func (*FeatureSpec_Domain) isFeatureSpec_DomainInfo() {} @@ -743,7 +743,7 @@ var file_feast_core_FeatureSet_proto_rawDesc = []byte{ 0x04, 0x73, 0x70, 0x65, 0x63, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, - 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0x82, 0x03, 0x0a, 0x0e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0x88, 0x03, 0x0a, 0x0e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, @@ -767,122 +767,124 @@ var file_feast_core_FeatureSet_proto_rawDesc = []byte{ 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5c, 0x0a, 0x0a, 0x45, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x0a, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1b, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x09, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x94, 0x0b, 0x0a, 0x0b, 0x46, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x0a, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x1b, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x09, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x73, - 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6e, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, + 0x22, 0x5c, 0x0a, 0x0a, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x45, + 0x6e, 0x75, 0x6d, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0xa0, + 0x0b, 0x0a, 0x0b, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x70, 0x65, 0x63, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x45, + 0x6e, 0x75, 0x6d, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3b, + 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, + 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x45, 0x0a, 0x08, 0x70, + 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x50, 0x72, + 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, + 0x63, 0x65, 0x12, 0x5b, 0x0a, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x72, 0x65, 0x73, + 0x65, 0x6e, 0x63, 0x65, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, - 0x6e, 0x63, 0x65, 0x48, 0x00, 0x52, 0x08, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, - 0x5b, 0x0a, 0x0e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, - 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, - 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, - 0x57, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x00, 0x52, 0x0d, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x05, - 0x73, 0x68, 0x61, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, - 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x68, 0x61, 0x70, 0x65, 0x48, - 0x01, 0x52, 0x05, 0x73, 0x68, 0x61, 0x70, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x48, 0x01, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, - 0x18, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x02, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x42, 0x0a, 0x0a, 0x69, 0x6e, 0x74, - 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, - 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x49, 0x6e, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x48, 0x02, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x48, 0x0a, - 0x0c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x46, 0x6c, 0x6f, - 0x61, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0b, 0x66, 0x6c, 0x6f, 0x61, - 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x4b, 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x69, 0x6e, - 0x67, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x6e, 0x63, 0x65, 0x57, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x00, + 0x52, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x3a, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x70, 0x65, 0x18, 0x20, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x6f, - 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x45, 0x0a, 0x0b, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6e, 0x73, - 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x76, 0x30, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, - 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x4b, 0x0a, 0x0d, 0x73, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0c, 0x73, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x67, 0x0a, 0x17, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x61, 0x6c, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, 0x65, 0x6e, 0x73, - 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, - 0x76, 0x30, 0x2e, 0x4e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x15, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x61, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x12, 0x48, 0x0a, 0x0c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, - 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, - 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0b, - 0x69, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x42, 0x0a, 0x0a, 0x6d, - 0x69, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x49, 0x44, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x48, 0x02, 0x52, 0x09, 0x6d, 0x69, 0x64, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, - 0x42, 0x0a, 0x0a, 0x75, 0x72, 0x6c, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x55, 0x52, 0x4c, - 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x09, 0x75, 0x72, 0x6c, 0x44, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x12, 0x45, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, - 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, - 0x30, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0a, - 0x74, 0x69, 0x6d, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x56, 0x0a, 0x12, 0x74, 0x69, - 0x6d, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, + 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x53, 0x68, 0x61, + 0x70, 0x65, 0x48, 0x01, 0x52, 0x05, 0x73, 0x68, 0x61, 0x70, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x21, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x48, 0x01, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x22, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x42, 0x0a, 0x0a, + 0x69, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x49, 0x6e, 0x74, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x09, 0x69, 0x6e, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x12, 0x48, 0x0a, 0x0c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x24, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x44, 0x61, 0x79, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, - 0x02, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x44, 0x61, 0x79, 0x44, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x12, 0x3b, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x13, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, - 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x70, 0x65, 0x63, 0x2e, 0x4c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, - 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x16, 0x0a, 0x14, 0x70, 0x72, - 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, - 0x74, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x70, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x42, 0x0d, 0x0a, 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, - 0x8f, 0x01, 0x0a, 0x0e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x12, 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x34, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x66, 0x65, - 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x2a, 0x4c, 0x0a, 0x10, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, - 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, 0x0a, - 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, 0x02, 0x42, - 0x4e, 0x0a, 0x0a, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x0f, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, - 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0b, 0x66, + 0x6c, 0x6f, 0x61, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x4b, 0x0a, 0x0d, 0x73, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x25, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x45, 0x0a, 0x0b, 0x62, 0x6f, 0x6f, 0x6c, 0x5f, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x48, 0x02, 0x52, 0x0a, 0x62, 0x6f, 0x6f, 0x6c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x4b, + 0x0a, 0x0d, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, + 0x27, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, + 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x53, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0c, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x67, 0x0a, 0x17, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x74, + 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x4e, 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x4c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x15, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x61, 0x6c, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x44, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x48, 0x0a, 0x0c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x64, 0x6f, + 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x74, 0x65, 0x6e, + 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x2e, 0x76, 0x30, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, + 0x02, 0x52, 0x0b, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x42, + 0x0a, 0x0a, 0x6d, 0x69, 0x64, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x2a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x49, 0x44, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x09, 0x6d, 0x69, 0x64, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x12, 0x42, 0x0a, 0x0a, 0x75, 0x72, 0x6c, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x18, 0x2b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, + 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x2e, + 0x55, 0x52, 0x4c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x09, 0x75, 0x72, 0x6c, + 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x45, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x64, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x2c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x74, 0x65, + 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x76, 0x30, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x48, + 0x02, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x56, 0x0a, + 0x12, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6f, 0x66, 0x5f, 0x64, 0x61, 0x79, 0x5f, 0x64, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x18, 0x2d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x74, 0x65, 0x6e, 0x73, + 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, + 0x76, 0x30, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x44, 0x61, 0x79, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x48, 0x02, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x4f, 0x66, 0x44, 0x61, 0x79, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x42, 0x16, 0x0a, 0x14, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x73, 0x68, 0x61, 0x70, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x10, 0x4a, 0x04, 0x08, 0x11, 0x10, + 0x1e, 0x22, 0x8f, 0x01, 0x0a, 0x0e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x34, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, + 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x53, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x2a, 0x4c, 0x0a, 0x10, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, + 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x41, 0x44, 0x59, 0x10, + 0x02, 0x42, 0x58, 0x0a, 0x10, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x0f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, + 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, + 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -938,22 +940,22 @@ var file_feast_core_FeatureSet_proto_depIdxs = []int32{ 6, // 6: feast.core.FeatureSetSpec.labels:type_name -> feast.core.FeatureSetSpec.LabelsEntry 10, // 7: feast.core.EntitySpec.value_type:type_name -> feast.types.ValueType.Enum 10, // 8: feast.core.FeatureSpec.value_type:type_name -> feast.types.ValueType.Enum - 11, // 9: feast.core.FeatureSpec.presence:type_name -> tensorflow.metadata.v0.FeaturePresence - 12, // 10: feast.core.FeatureSpec.group_presence:type_name -> tensorflow.metadata.v0.FeaturePresenceWithinGroup - 13, // 11: feast.core.FeatureSpec.shape:type_name -> tensorflow.metadata.v0.FixedShape - 14, // 12: feast.core.FeatureSpec.value_count:type_name -> tensorflow.metadata.v0.ValueCount - 15, // 13: feast.core.FeatureSpec.int_domain:type_name -> tensorflow.metadata.v0.IntDomain - 16, // 14: feast.core.FeatureSpec.float_domain:type_name -> tensorflow.metadata.v0.FloatDomain - 17, // 15: feast.core.FeatureSpec.string_domain:type_name -> tensorflow.metadata.v0.StringDomain - 18, // 16: feast.core.FeatureSpec.bool_domain:type_name -> tensorflow.metadata.v0.BoolDomain - 19, // 17: feast.core.FeatureSpec.struct_domain:type_name -> tensorflow.metadata.v0.StructDomain - 20, // 18: feast.core.FeatureSpec.natural_language_domain:type_name -> tensorflow.metadata.v0.NaturalLanguageDomain - 21, // 19: feast.core.FeatureSpec.image_domain:type_name -> tensorflow.metadata.v0.ImageDomain - 22, // 20: feast.core.FeatureSpec.mid_domain:type_name -> tensorflow.metadata.v0.MIDDomain - 23, // 21: feast.core.FeatureSpec.url_domain:type_name -> tensorflow.metadata.v0.URLDomain - 24, // 22: feast.core.FeatureSpec.time_domain:type_name -> tensorflow.metadata.v0.TimeDomain - 25, // 23: feast.core.FeatureSpec.time_of_day_domain:type_name -> tensorflow.metadata.v0.TimeOfDayDomain - 7, // 24: feast.core.FeatureSpec.labels:type_name -> feast.core.FeatureSpec.LabelsEntry + 7, // 9: feast.core.FeatureSpec.labels:type_name -> feast.core.FeatureSpec.LabelsEntry + 11, // 10: feast.core.FeatureSpec.presence:type_name -> tensorflow.metadata.v0.FeaturePresence + 12, // 11: feast.core.FeatureSpec.group_presence:type_name -> tensorflow.metadata.v0.FeaturePresenceWithinGroup + 13, // 12: feast.core.FeatureSpec.shape:type_name -> tensorflow.metadata.v0.FixedShape + 14, // 13: feast.core.FeatureSpec.value_count:type_name -> tensorflow.metadata.v0.ValueCount + 15, // 14: feast.core.FeatureSpec.int_domain:type_name -> tensorflow.metadata.v0.IntDomain + 16, // 15: feast.core.FeatureSpec.float_domain:type_name -> tensorflow.metadata.v0.FloatDomain + 17, // 16: feast.core.FeatureSpec.string_domain:type_name -> tensorflow.metadata.v0.StringDomain + 18, // 17: feast.core.FeatureSpec.bool_domain:type_name -> tensorflow.metadata.v0.BoolDomain + 19, // 18: feast.core.FeatureSpec.struct_domain:type_name -> tensorflow.metadata.v0.StructDomain + 20, // 19: feast.core.FeatureSpec.natural_language_domain:type_name -> tensorflow.metadata.v0.NaturalLanguageDomain + 21, // 20: feast.core.FeatureSpec.image_domain:type_name -> tensorflow.metadata.v0.ImageDomain + 22, // 21: feast.core.FeatureSpec.mid_domain:type_name -> tensorflow.metadata.v0.MIDDomain + 23, // 22: feast.core.FeatureSpec.url_domain:type_name -> tensorflow.metadata.v0.URLDomain + 24, // 23: feast.core.FeatureSpec.time_domain:type_name -> tensorflow.metadata.v0.TimeDomain + 25, // 24: feast.core.FeatureSpec.time_of_day_domain:type_name -> tensorflow.metadata.v0.TimeOfDayDomain 26, // 25: feast.core.FeatureSetMeta.created_timestamp:type_name -> google.protobuf.Timestamp 0, // 26: feast.core.FeatureSetMeta.status:type_name -> feast.core.FeatureSetStatus 27, // [27:27] is the sub-list for method output_type diff --git a/sdk/go/protos/feast/core/FeatureSetReference.pb.go b/sdk/go/protos/feast/core/FeatureSetReference.pb.go index 1667565bd23..3da39ad922a 100644 --- a/sdk/go/protos/feast/core/FeatureSetReference.pb.go +++ b/sdk/go/protos/feast/core/FeatureSetReference.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/FeatureSetReference.proto @@ -105,15 +105,16 @@ var file_feast_core_FeatureSetReference_proto_rawDesc = []byte{ 0x0a, 0x24, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, - 0x72, 0x65, 0x22, 0x43, 0x0a, 0x13, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, + 0x72, 0x65, 0x22, 0x49, 0x0a, 0x13, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x57, 0x0a, 0x0a, 0x66, 0x65, 0x61, 0x73, 0x74, - 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x18, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, - 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, - 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, - 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x42, 0x61, 0x0a, + 0x10, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x72, + 0x65, 0x42, 0x18, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x33, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, + 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } diff --git a/sdk/go/protos/feast/core/IngestionJob.pb.go b/sdk/go/protos/feast/core/IngestionJob.pb.go index 3623d95d885..bf30f25168f 100644 --- a/sdk/go/protos/feast/core/IngestionJob.pb.go +++ b/sdk/go/protos/feast/core/IngestionJob.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/IngestionJob.proto @@ -248,12 +248,13 @@ var file_feast_core_IngestionJob_proto_rawDesc = []byte{ 0x0a, 0x07, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x55, 0x53, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x55, 0x53, 0x50, 0x45, 0x4e, - 0x44, 0x45, 0x44, 0x10, 0x08, 0x42, 0x50, 0x0a, 0x0a, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x42, 0x11, 0x49, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, - 0x62, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, - 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, - 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x44, 0x45, 0x44, 0x10, 0x08, 0x42, 0x5a, 0x0a, 0x10, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x11, 0x49, 0x6e, 0x67, 0x65, 0x73, + 0x74, 0x69, 0x6f, 0x6e, 0x4a, 0x6f, 0x62, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x33, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, + 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/core/Runner.pb.go b/sdk/go/protos/feast/core/Runner.pb.go index ae9f7c4e7d3..54e03714d2a 100644 --- a/sdk/go/protos/feast/core/Runner.pb.go +++ b/sdk/go/protos/feast/core/Runner.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/Runner.proto @@ -290,12 +290,13 @@ var file_feast_core_Runner_proto_rawDesc = []byte{ 0x4e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x30, 0x0a, 0x13, 0x64, 0x65, 0x61, 0x64, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x70, 0x65, 0x63, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x64, 0x65, 0x61, 0x64, 0x4c, 0x65, 0x74, - 0x74, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x70, 0x65, 0x63, 0x42, 0x4a, 0x0a, 0x0a, - 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x0b, 0x52, 0x75, 0x6e, 0x6e, - 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, - 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, - 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x70, 0x65, 0x63, 0x42, 0x54, 0x0a, 0x10, + 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x42, 0x0b, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x33, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, + 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, + 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/core/Source.pb.go b/sdk/go/protos/feast/core/Source.pb.go index 368f50c5acb..b29dc5af96c 100644 --- a/sdk/go/protos/feast/core/Source.pb.go +++ b/sdk/go/protos/feast/core/Source.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/Source.proto @@ -266,12 +266,13 @@ var file_feast_core_Source_proto_rawDesc = []byte{ 0x28, 0x05, 0x52, 0x11, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x2a, 0x24, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, - 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x41, 0x46, 0x4b, 0x41, 0x10, 0x01, 0x42, 0x4a, 0x0a, 0x0a, 0x66, - 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x0b, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, - 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, - 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x09, 0x0a, 0x05, 0x4b, 0x41, 0x46, 0x4b, 0x41, 0x10, 0x01, 0x42, 0x54, 0x0a, 0x10, 0x66, + 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, + 0x0b, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x33, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, + 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/core/Store.pb.go b/sdk/go/protos/feast/core/Store.pb.go index f3b7728ab97..866dc71fe94 100644 --- a/sdk/go/protos/feast/core/Store.pb.go +++ b/sdk/go/protos/feast/core/Store.pb.go @@ -16,7 +16,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/core/Store.proto @@ -74,6 +74,7 @@ const ( // ====================|==================|================================ // - event_timestamp | TIMESTAMP | event time of the FeatureRow // - created_timestamp | TIMESTAMP | processing time of the ingestion of the FeatureRow + // - ingestion_id | STRING | unique id identifying groups of rows that have been ingested together // - job_id | STRING | identifier for the job that writes the FeatureRow to the corresponding BigQuery table // // BigQuery table created will be partitioned by the field "event_timestamp" @@ -642,7 +643,7 @@ var File_feast_core_Store_proto protoreflect.FileDescriptor var file_feast_core_Store_proto_rawDesc = []byte{ 0x0a, 0x16, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x63, 0x6f, 0x72, 0x65, 0x22, 0xae, 0x09, 0x0a, 0x05, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, + 0x63, 0x6f, 0x72, 0x65, 0x22, 0xb4, 0x09, 0x0a, 0x05, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x53, 0x74, @@ -707,20 +708,21 @@ var file_feast_core_Store_proto_rawDesc = []byte{ 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x10, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x42, 0x61, 0x63, 0x6b, 0x6f, 0x66, 0x66, 0x4d, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6d, 0x61, - 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x3c, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, + 0x78, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x1a, 0x42, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x53, 0x0a, 0x09, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, - 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x44, 0x49, 0x53, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x42, - 0x49, 0x47, 0x51, 0x55, 0x45, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x53, - 0x53, 0x41, 0x4e, 0x44, 0x52, 0x41, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x44, 0x49, - 0x53, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, 0x04, 0x42, 0x08, 0x0a, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x49, 0x0a, 0x0a, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x63, - 0x6f, 0x72, 0x65, 0x42, 0x0a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, - 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, - 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x53, 0x0a, 0x09, + 0x53, 0x74, 0x6f, 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, + 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x45, 0x44, 0x49, 0x53, 0x10, + 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x42, 0x49, 0x47, 0x51, 0x55, 0x45, 0x52, 0x59, 0x10, 0x02, 0x12, + 0x0d, 0x0a, 0x09, 0x43, 0x41, 0x53, 0x53, 0x41, 0x4e, 0x44, 0x52, 0x41, 0x10, 0x03, 0x12, 0x11, + 0x0a, 0x0d, 0x52, 0x45, 0x44, 0x49, 0x53, 0x5f, 0x43, 0x4c, 0x55, 0x53, 0x54, 0x45, 0x52, 0x10, + 0x04, 0x42, 0x08, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x53, 0x0a, 0x10, 0x66, + 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x42, + 0x0a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x33, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, + 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } diff --git a/sdk/go/protos/feast/serving/ServingService.pb.go b/sdk/go/protos/feast/serving/ServingService.pb.go index 0fd8c09bc71..9e5a151b7ee 100644 --- a/sdk/go/protos/feast/serving/ServingService.pb.go +++ b/sdk/go/protos/feast/serving/ServingService.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/serving/ServingService.proto @@ -25,7 +25,6 @@ import ( context "context" types "github.com/feast-dev/feast/sdk/go/protos/feast/types" proto "github.com/golang/protobuf/proto" - duration "github.com/golang/protobuf/ptypes/duration" timestamp "github.com/golang/protobuf/ptypes/timestamp" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" @@ -355,16 +354,14 @@ type FeatureReference struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Project name + // Project name. This field is optional, if unspecified will default to 'default'. Project string `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` // Feature name Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // The features will be retrieved if: - // entity_timestamp - max_age <= event_timestamp <= entity_timestamp - // - // If unspecified the default max_age specified in FeatureSetSpec will - // be used. - MaxAge *duration.Duration `protobuf:"bytes,4,opt,name=max_age,json=maxAge,proto3" json:"max_age,omitempty"` + // Feature set name specifying the feature set of this referenced feature. + // This field is optional if the feature referenced is unique across the project + // in which case the feature set would be automatically infered + FeatureSet string `protobuf:"bytes,5,opt,name=feature_set,json=featureSet,proto3" json:"feature_set,omitempty"` } func (x *FeatureReference) Reset() { @@ -413,11 +410,11 @@ func (x *FeatureReference) GetName() string { return "" } -func (x *FeatureReference) GetMaxAge() *duration.Duration { +func (x *FeatureReference) GetFeatureSet() string { if x != nil { - return x.MaxAge + return x.FeatureSet } - return nil + return "" } type GetOnlineFeaturesRequest struct { @@ -1071,8 +1068,6 @@ var file_feast_serving_ServingService_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, @@ -1086,165 +1081,165 @@ var file_feast_serving_ServingService_proto_rawDesc = []byte{ 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6a, 0x6f, 0x62, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6a, 0x6f, 0x62, 0x53, 0x74, 0x61, - 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x74, 0x0a, 0x10, + 0x67, 0x69, 0x6e, 0x67, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x10, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x32, - 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6d, 0x61, 0x78, 0x41, - 0x67, 0x65, 0x22, 0xe1, 0x03, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, - 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x3b, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, - 0x67, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, - 0x63, 0x65, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x0b, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x31, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, + 0x0a, 0x0b, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x4a, + 0x04, 0x08, 0x03, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x22, 0xe1, 0x03, 0x0a, 0x18, + 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x66, 0x65, 0x61, + 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, + 0x72, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x66, 0x65, 0x61, + 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, + 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x6f, 0x77, 0x52, 0x0a, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x6f, 0x77, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x6f, 0x6d, 0x69, + 0x74, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x6f, 0x6d, + 0x69, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0xf8, 0x01, 0x0a, 0x09, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, + 0x6f, 0x77, 0x12, 0x45, 0x0a, 0x10, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x55, 0x0a, 0x06, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x66, 0x65, 0x61, 0x73, + 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x6f, 0x77, 0x2e, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x1a, 0x4d, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x9b, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, + 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x08, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x65, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, + 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xad, 0x02, + 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x34, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x52, 0x6f, 0x77, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x6f, 0x77, 0x73, - 0x12, 0x39, 0x0a, 0x19, 0x6f, 0x6d, 0x69, 0x74, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x16, 0x6f, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, - 0x73, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x1a, 0xf8, 0x01, 0x0a, 0x09, - 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x6f, 0x77, 0x12, 0x45, 0x0a, 0x10, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x12, 0x55, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3d, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, - 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x52, 0x6f, 0x77, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x4d, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9b, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x42, 0x61, - 0x74, 0x63, 0x68, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, - 0x43, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x22, 0xad, 0x02, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, - 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x0b, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xb6, 0x01, 0x0a, 0x0b, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x66, 0x65, - 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, - 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x4d, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x40, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xb6, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x4d, + 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x40, 0x0a, + 0x18, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6a, 0x6f, 0x62, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, + 0x35, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, - 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0x35, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0x36, 0x0a, - 0x0e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x24, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, - 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, - 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0xe2, 0x01, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x66, 0x65, - 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x66, 0x65, 0x61, 0x73, - 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, - 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, 0x72, 0x69, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x69, 0x73, 0x12, 0x3a, - 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0a, - 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0xd4, 0x01, 0x0a, 0x0d, 0x44, - 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0b, - 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x27, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, - 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x69, - 0x6c, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a, 0x65, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, - 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, - 0x72, 0x69, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, - 0x72, 0x69, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x42, - 0x10, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x2a, 0x6f, 0x0a, 0x10, 0x46, 0x65, 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, - 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x46, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x53, - 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, - 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x46, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x53, - 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x4e, 0x4c, 0x49, - 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x46, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x53, 0x45, - 0x52, 0x56, 0x49, 0x4e, 0x47, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, - 0x10, 0x02, 0x2a, 0x36, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, - 0x10, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, - 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x01, 0x2a, 0x68, 0x0a, 0x09, 0x4a, 0x6f, - 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x12, 0x4a, 0x4f, 0x42, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, - 0x16, 0x0a, 0x12, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, - 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4a, 0x4f, 0x42, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, - 0x13, 0x0a, 0x0f, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x4f, - 0x4e, 0x45, 0x10, 0x03, 0x2a, 0x3b, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x12, 0x17, 0x0a, 0x13, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, - 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x44, - 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x41, 0x56, 0x52, 0x4f, 0x10, - 0x01, 0x32, 0x92, 0x03, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x6c, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x73, 0x74, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x2e, 0x66, 0x65, - 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x46, - 0x65, 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x73, 0x74, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x66, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x27, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, - 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x28, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0x36, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6a, 0x6f, 0x62, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x03, 0x6a, 0x6f, 0x62, 0x22, 0xe2, + 0x01, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x30, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x6e, 0x67, 0x2e, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x75, 0x72, 0x69, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x66, + 0x69, 0x6c, 0x65, 0x55, 0x72, 0x69, 0x73, 0x12, 0x3a, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x66, + 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x61, 0x74, + 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x22, 0xd4, 0x01, 0x0a, 0x0d, 0x44, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x66, 0x65, 0x61, + 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x73, + 0x65, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x1a, 0x65, 0x0a, 0x0a, 0x46, 0x69, 0x6c, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, 0x72, 0x69, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x69, 0x73, 0x12, 0x3a, 0x0a, 0x0b, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x19, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, + 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0a, 0x64, 0x61, + 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x61, + 0x73, 0x65, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2a, 0x6f, 0x0a, 0x10, 0x46, 0x65, + 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, + 0x0a, 0x1a, 0x46, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x1d, + 0x0a, 0x19, 0x46, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x1c, 0x0a, + 0x18, 0x46, 0x45, 0x41, 0x53, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x54, 0x43, 0x48, 0x10, 0x02, 0x2a, 0x36, 0x0a, 0x07, 0x4a, + 0x6f, 0x62, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, + 0x4a, 0x4f, 0x42, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x4c, 0x4f, 0x41, + 0x44, 0x10, 0x01, 0x2a, 0x68, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x16, 0x0a, 0x12, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, + 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x4a, 0x4f, 0x42, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, + 0x12, 0x16, 0x0a, 0x12, 0x4a, 0x4f, 0x42, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, + 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4a, 0x4f, 0x42, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x03, 0x2a, 0x3b, 0x0a, + 0x0a, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x17, 0x0a, 0x13, 0x44, + 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, + 0x49, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, + 0x4d, 0x41, 0x54, 0x5f, 0x41, 0x56, 0x52, 0x4f, 0x10, 0x01, 0x32, 0x92, 0x03, 0x0a, 0x0e, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6c, 0x0a, + 0x13, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x29, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, + 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x12, 0x27, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x10, 0x47, 0x65, - 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x26, - 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, - 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x45, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x66, 0x65, 0x61, 0x73, - 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x0a, 0x0d, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x42, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, - 0x41, 0x50, 0x49, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, - 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, - 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x66, 0x65, 0x61, 0x73, + 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x6e, 0x6c, + 0x69, 0x6e, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x26, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x27, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x2e, + 0x47, 0x65, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4a, + 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1d, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, + 0x2e, 0x47, 0x65, 0x74, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, + 0x5e, 0x0a, 0x13, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x42, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x41, + 0x50, 0x49, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, + 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1282,44 +1277,42 @@ var file_feast_serving_ServingService_proto_goTypes = []interface{}{ (*GetOnlineFeaturesResponse_FieldValues)(nil), // 17: feast.serving.GetOnlineFeaturesResponse.FieldValues nil, // 18: feast.serving.GetOnlineFeaturesResponse.FieldValues.FieldsEntry (*DatasetSource_FileSource)(nil), // 19: feast.serving.DatasetSource.FileSource - (*duration.Duration)(nil), // 20: google.protobuf.Duration - (*timestamp.Timestamp)(nil), // 21: google.protobuf.Timestamp - (*types.Value)(nil), // 22: feast.types.Value + (*timestamp.Timestamp)(nil), // 20: google.protobuf.Timestamp + (*types.Value)(nil), // 21: feast.types.Value } var file_feast_serving_ServingService_proto_depIdxs = []int32{ 0, // 0: feast.serving.GetFeastServingInfoResponse.type:type_name -> feast.serving.FeastServingType - 20, // 1: feast.serving.FeatureReference.max_age:type_name -> google.protobuf.Duration - 6, // 2: feast.serving.GetOnlineFeaturesRequest.features:type_name -> feast.serving.FeatureReference - 15, // 3: feast.serving.GetOnlineFeaturesRequest.entity_rows:type_name -> feast.serving.GetOnlineFeaturesRequest.EntityRow - 6, // 4: feast.serving.GetBatchFeaturesRequest.features:type_name -> feast.serving.FeatureReference - 14, // 5: feast.serving.GetBatchFeaturesRequest.dataset_source:type_name -> feast.serving.DatasetSource - 17, // 6: feast.serving.GetOnlineFeaturesResponse.field_values:type_name -> feast.serving.GetOnlineFeaturesResponse.FieldValues - 13, // 7: feast.serving.GetBatchFeaturesResponse.job:type_name -> feast.serving.Job - 13, // 8: feast.serving.GetJobRequest.job:type_name -> feast.serving.Job - 13, // 9: feast.serving.GetJobResponse.job:type_name -> feast.serving.Job - 1, // 10: feast.serving.Job.type:type_name -> feast.serving.JobType - 2, // 11: feast.serving.Job.status:type_name -> feast.serving.JobStatus - 3, // 12: feast.serving.Job.data_format:type_name -> feast.serving.DataFormat - 19, // 13: feast.serving.DatasetSource.file_source:type_name -> feast.serving.DatasetSource.FileSource - 21, // 14: feast.serving.GetOnlineFeaturesRequest.EntityRow.entity_timestamp:type_name -> google.protobuf.Timestamp - 16, // 15: feast.serving.GetOnlineFeaturesRequest.EntityRow.fields:type_name -> feast.serving.GetOnlineFeaturesRequest.EntityRow.FieldsEntry - 22, // 16: feast.serving.GetOnlineFeaturesRequest.EntityRow.FieldsEntry.value:type_name -> feast.types.Value - 18, // 17: feast.serving.GetOnlineFeaturesResponse.FieldValues.fields:type_name -> feast.serving.GetOnlineFeaturesResponse.FieldValues.FieldsEntry - 22, // 18: feast.serving.GetOnlineFeaturesResponse.FieldValues.FieldsEntry.value:type_name -> feast.types.Value - 3, // 19: feast.serving.DatasetSource.FileSource.data_format:type_name -> feast.serving.DataFormat - 4, // 20: feast.serving.ServingService.GetFeastServingInfo:input_type -> feast.serving.GetFeastServingInfoRequest - 7, // 21: feast.serving.ServingService.GetOnlineFeatures:input_type -> feast.serving.GetOnlineFeaturesRequest - 8, // 22: feast.serving.ServingService.GetBatchFeatures:input_type -> feast.serving.GetBatchFeaturesRequest - 11, // 23: feast.serving.ServingService.GetJob:input_type -> feast.serving.GetJobRequest - 5, // 24: feast.serving.ServingService.GetFeastServingInfo:output_type -> feast.serving.GetFeastServingInfoResponse - 9, // 25: feast.serving.ServingService.GetOnlineFeatures:output_type -> feast.serving.GetOnlineFeaturesResponse - 10, // 26: feast.serving.ServingService.GetBatchFeatures:output_type -> feast.serving.GetBatchFeaturesResponse - 12, // 27: feast.serving.ServingService.GetJob:output_type -> feast.serving.GetJobResponse - 24, // [24:28] is the sub-list for method output_type - 20, // [20:24] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 6, // 1: feast.serving.GetOnlineFeaturesRequest.features:type_name -> feast.serving.FeatureReference + 15, // 2: feast.serving.GetOnlineFeaturesRequest.entity_rows:type_name -> feast.serving.GetOnlineFeaturesRequest.EntityRow + 6, // 3: feast.serving.GetBatchFeaturesRequest.features:type_name -> feast.serving.FeatureReference + 14, // 4: feast.serving.GetBatchFeaturesRequest.dataset_source:type_name -> feast.serving.DatasetSource + 17, // 5: feast.serving.GetOnlineFeaturesResponse.field_values:type_name -> feast.serving.GetOnlineFeaturesResponse.FieldValues + 13, // 6: feast.serving.GetBatchFeaturesResponse.job:type_name -> feast.serving.Job + 13, // 7: feast.serving.GetJobRequest.job:type_name -> feast.serving.Job + 13, // 8: feast.serving.GetJobResponse.job:type_name -> feast.serving.Job + 1, // 9: feast.serving.Job.type:type_name -> feast.serving.JobType + 2, // 10: feast.serving.Job.status:type_name -> feast.serving.JobStatus + 3, // 11: feast.serving.Job.data_format:type_name -> feast.serving.DataFormat + 19, // 12: feast.serving.DatasetSource.file_source:type_name -> feast.serving.DatasetSource.FileSource + 20, // 13: feast.serving.GetOnlineFeaturesRequest.EntityRow.entity_timestamp:type_name -> google.protobuf.Timestamp + 16, // 14: feast.serving.GetOnlineFeaturesRequest.EntityRow.fields:type_name -> feast.serving.GetOnlineFeaturesRequest.EntityRow.FieldsEntry + 21, // 15: feast.serving.GetOnlineFeaturesRequest.EntityRow.FieldsEntry.value:type_name -> feast.types.Value + 18, // 16: feast.serving.GetOnlineFeaturesResponse.FieldValues.fields:type_name -> feast.serving.GetOnlineFeaturesResponse.FieldValues.FieldsEntry + 21, // 17: feast.serving.GetOnlineFeaturesResponse.FieldValues.FieldsEntry.value:type_name -> feast.types.Value + 3, // 18: feast.serving.DatasetSource.FileSource.data_format:type_name -> feast.serving.DataFormat + 4, // 19: feast.serving.ServingService.GetFeastServingInfo:input_type -> feast.serving.GetFeastServingInfoRequest + 7, // 20: feast.serving.ServingService.GetOnlineFeatures:input_type -> feast.serving.GetOnlineFeaturesRequest + 8, // 21: feast.serving.ServingService.GetBatchFeatures:input_type -> feast.serving.GetBatchFeaturesRequest + 11, // 22: feast.serving.ServingService.GetJob:input_type -> feast.serving.GetJobRequest + 5, // 23: feast.serving.ServingService.GetFeastServingInfo:output_type -> feast.serving.GetFeastServingInfoResponse + 9, // 24: feast.serving.ServingService.GetOnlineFeatures:output_type -> feast.serving.GetOnlineFeaturesResponse + 10, // 25: feast.serving.ServingService.GetBatchFeatures:output_type -> feast.serving.GetBatchFeaturesResponse + 12, // 26: feast.serving.ServingService.GetJob:output_type -> feast.serving.GetJobResponse + 23, // [23:27] is the sub-list for method output_type + 19, // [19:23] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_feast_serving_ServingService_proto_init() } diff --git a/sdk/go/protos/feast/storage/Redis.pb.go b/sdk/go/protos/feast/storage/Redis.pb.go index 42fd0077ec8..cfc22a458e5 100644 --- a/sdk/go/protos/feast/storage/Redis.pb.go +++ b/sdk/go/protos/feast/storage/Redis.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/storage/Redis.proto @@ -113,12 +113,13 @@ var file_feast_storage_Redis_proto_rawDesc = []byte{ 0x12, 0x2e, 0x0a, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, - 0x42, 0x4f, 0x0a, 0x0d, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x42, 0x0a, 0x52, 0x65, 0x64, 0x69, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x32, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, - 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x42, 0x59, 0x0a, 0x13, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x42, 0x0a, 0x52, 0x65, 0x64, 0x69, 0x73, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, + 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, + 0x61, 0x73, 0x74, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/types/FeatureRow.pb.go b/sdk/go/protos/feast/types/FeatureRow.pb.go index 696f138459f..3b42b44889e 100644 --- a/sdk/go/protos/feast/types/FeatureRow.pb.go +++ b/sdk/go/protos/feast/types/FeatureRow.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/types/FeatureRow.proto @@ -56,6 +56,8 @@ type FeatureRow struct { // /. This value will be used by the feast ingestion job to filter // rows, and write the values to the correct tables. FeatureSet string `protobuf:"bytes,6,opt,name=feature_set,json=featureSet,proto3" json:"feature_set,omitempty"` + // Identifier tying this feature row to a specific ingestion job. + IngestionId string `protobuf:"bytes,7,opt,name=ingestion_id,json=ingestionId,proto3" json:"ingestion_id,omitempty"` } func (x *FeatureRow) Reset() { @@ -111,6 +113,13 @@ func (x *FeatureRow) GetFeatureSet() string { return "" } +func (x *FeatureRow) GetIngestionId() string { + if x != nil { + return x.IngestionId + } + return "" +} + var File_feast_types_FeatureRow_proto protoreflect.FileDescriptor var file_feast_types_FeatureRow_proto_rawDesc = []byte{ @@ -120,7 +129,7 @@ var file_feast_types_FeatureRow_proto_rawDesc = []byte{ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc1, 0x01, 0x0a, 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x6f, 0x77, 0x12, 0x2a, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, @@ -130,12 +139,15 @@ var file_feast_types_FeatureRow_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, 0x65, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x42, 0x50, 0x0a, 0x0b, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x42, 0x0f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x6f, - 0x77, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, - 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, - 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x67, 0x65, 0x73, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, + 0x67, 0x65, 0x73, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x5a, 0x0a, 0x11, 0x66, 0x65, 0x61, + 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x42, 0x0f, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x6f, 0x77, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, + 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, + 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, + 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/types/FeatureRowExtended.pb.go b/sdk/go/protos/feast/types/FeatureRowExtended.pb.go index 8ca9ee1bc90..1692a55dd4f 100644 --- a/sdk/go/protos/feast/types/FeatureRowExtended.pb.go +++ b/sdk/go/protos/feast/types/FeatureRowExtended.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/types/FeatureRowExtended.proto @@ -264,13 +264,13 @@ var file_feast_types_FeatureRowExtended_proto_rawDesc = []byte{ 0x73, 0x65, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, - 0x6e, 0x42, 0x58, 0x0a, 0x0b, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x42, 0x17, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, - 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, - 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6e, 0x42, 0x62, 0x0a, 0x11, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x42, 0x17, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, + 0x6f, 0x77, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, + 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, + 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, + 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/types/Field.pb.go b/sdk/go/protos/feast/types/Field.pb.go index c7b5193db5d..901075e2620 100644 --- a/sdk/go/protos/feast/types/Field.pb.go +++ b/sdk/go/protos/feast/types/Field.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/types/Field.proto @@ -106,12 +106,13 @@ var file_feast_types_Field_proto_rawDesc = []byte{ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x4b, 0x0a, 0x0b, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x42, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, - 0x6a, 0x65, 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x55, 0x0a, 0x11, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x42, 0x0a, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, + 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/go/protos/feast/types/Value.pb.go b/sdk/go/protos/feast/types/Value.pb.go index ba530b2dac2..317768fd549 100644 --- a/sdk/go/protos/feast/types/Value.pb.go +++ b/sdk/go/protos/feast/types/Value.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: feast/types/Value.proto @@ -819,12 +819,13 @@ var file_feast_types_Value_proto_rawDesc = []byte{ 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x02, 0x52, 0x03, 0x76, 0x61, 0x6c, 0x22, 0x1c, 0x0a, 0x08, 0x42, 0x6f, 0x6f, 0x6c, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x76, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28, 0x08, 0x52, 0x03, 0x76, - 0x61, 0x6c, 0x42, 0x4b, 0x0a, 0x0b, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x42, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x30, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, - 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x42, 0x55, 0x0a, 0x11, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x42, 0x0a, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, + 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x66, 0x65, + 0x61, 0x73, 0x74, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/sdk/go/protos/tensorflow_metadata/proto/v0/path.pb.go b/sdk/go/protos/tensorflow_metadata/proto/v0/path.pb.go index d609c5e89c0..fe4cc3ee47a 100644 --- a/sdk/go/protos/tensorflow_metadata/proto/v0/path.pb.go +++ b/sdk/go/protos/tensorflow_metadata/proto/v0/path.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: tensorflow_metadata/proto/v0/path.proto @@ -112,14 +112,14 @@ var file_tensorflow_metadata_proto_v0_path_proto_rawDesc = []byte{ 0x61, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x22, 0x1a, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65, - 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x42, 0x64, 0x0a, + 0x70, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x73, 0x74, 0x65, 0x70, 0x42, 0x68, 0x0a, 0x1a, 0x6f, 0x72, 0x67, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x50, 0x01, 0x5a, 0x41, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, 0x6b, 0x2f, - 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x30, - 0xf8, 0x01, 0x01, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x50, 0x01, 0x5a, 0x45, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2d, + 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2f, 0x76, 0x30, 0xf8, 0x01, 0x01, } var ( diff --git a/sdk/go/protos/tensorflow_metadata/proto/v0/schema.pb.go b/sdk/go/protos/tensorflow_metadata/proto/v0/schema.pb.go index ab7ffc7201d..97117947b78 100644 --- a/sdk/go/protos/tensorflow_metadata/proto/v0/schema.pb.go +++ b/sdk/go/protos/tensorflow_metadata/proto/v0/schema.pb.go @@ -15,7 +15,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.21.0 +// protoc-gen-go v1.23.0 // protoc v3.10.0 // source: tensorflow_metadata/proto/v0/schema.proto @@ -3476,13 +3476,13 @@ var file_tensorflow_metadata_proto_v0_schema_proto_rawDesc = []byte{ 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x59, 0x54, 0x45, 0x53, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x55, 0x43, 0x54, 0x10, 0x04, 0x42, - 0x64, 0x0a, 0x1a, 0x6f, 0x72, 0x67, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, + 0x68, 0x0a, 0x1a, 0x6f, 0x72, 0x67, 0x2e, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x76, 0x30, 0x50, 0x01, 0x5a, - 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6a, 0x65, - 0x6b, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, 0x67, 0x6f, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, 0x66, 0x6c, 0x6f, 0x77, - 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x76, 0x30, 0xf8, 0x01, 0x01, + 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x65, 0x61, 0x73, + 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x66, 0x65, 0x61, 0x73, 0x74, 0x2f, 0x73, 0x64, 0x6b, 0x2f, + 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x74, 0x65, 0x6e, 0x73, 0x6f, 0x72, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x30, 0xf8, 0x01, 0x01, } var ( diff --git a/sdk/go/request.go b/sdk/go/request.go index f4e6597a9db..683f17c16ed 100644 --- a/sdk/go/request.go +++ b/sdk/go/request.go @@ -7,29 +7,29 @@ import ( ) var ( - // ErrInvalidFeatureName indicates that the user has provided a feature reference with the wrong structure or contents - ErrInvalidFeatureName = "invalid feature references %s provided, feature names must be in the format /" + // ErrInvalidFeatureRef indicates that the user has provided a feature reference + // with the wrong structure or contents + ErrInvalidFeatureRef = "Invalid Feature Reference %s provided, " + + "feature reference must be in the format [featureset:]name" ) // OnlineFeaturesRequest wrapper on feast.serving.GetOnlineFeaturesRequest. type OnlineFeaturesRequest struct { // Features is the list of features to obtain from Feast. Each feature can be given as - // - // / - // The only required components are the feature name and project. + // the format feature_set:feature, where "feature_set" & "feature" are feature set name + // and feature name respectively. The only required components is feature name. Features []string // Entities is the list of entity rows to retrieve features on. Each row is a map of entity name to entity value. Entities []Row - // Project is the default project to use when looking up features. This is only used when a project is not found - // within the feature id. + // Project specifies the project would contain the feature sets where the requested features belong to. Project string } // Builds the feast-specified request payload from the wrapper. func (r OnlineFeaturesRequest) buildRequest() (*serving.GetOnlineFeaturesRequest, error) { - features, err := buildFeatures(r.Features, r.Project) + featureRefs, err := buildFeatureRefs(r.Features, r.Project) if err != nil { return nil, err } @@ -42,41 +42,74 @@ func (r OnlineFeaturesRequest) buildRequest() (*serving.GetOnlineFeaturesRequest } } return &serving.GetOnlineFeaturesRequest{ - Features: features, + Features: featureRefs, EntityRows: entityRows, }, nil } -// buildFeatures create a slice of FeatureReferences from a slice of "/" -// It returns an error when the format is invalid -func buildFeatures(featureReferences []string, defaultProject string) ([]*serving.FeatureReference, error) { - var features []*serving.FeatureReference +// Creates a slice of FeatureReferences from string representation in +// the format featureset:feature. +// featureRefStrs - string feature references to parse. +// project - Optionally sets the project in parsed FeatureReferences. Otherwise pass "" +// Returns parsed FeatureReferences. +// Returns an error when the format of the string feature reference is invalid +func buildFeatureRefs(featureRefStrs []string, project string) ([]*serving.FeatureReference, error) { + var featureRefs []*serving.FeatureReference - for _, featureRef := range featureReferences { - var project string - var name string + for _, featureRefStr := range featureRefStrs { + featureRef, err := parseFeatureRef(featureRefStr, false) + if err != nil { + return nil, err + } + // apply project if specified + if len(project) != 0 { + featureRef.Project = project + } + featureRefs = append(featureRefs, featureRef) + } + return featureRefs, nil +} - projectSplit := strings.Split(featureRef, "/") +// Parses a string FeatureReference into FeatureReference proto +// featureRefStr - the string feature reference to parse. +// ignoreProject - if true would ignore if project is specified in the given featureRefStr +// Otherwise, would return an error if project is detected in featureRefStr. +// Returns parsed FeatureReference. +// Returns an error when the format of the string feature reference is invalid +func parseFeatureRef(featureRefStr string, ignoreProject bool) (*serving.FeatureReference, error) { + if len(featureRefStr) == 0 { + return nil, fmt.Errorf(ErrInvalidFeatureRef, featureRefStr) + } - if len(projectSplit) == 2 { - project = projectSplit[0] - name = projectSplit[1] - } else if len(projectSplit) == 1 { - project = defaultProject - name = projectSplit[0] + var featureRef serving.FeatureReference + if strings.Contains(featureRefStr, "/") { + if ignoreProject { + projectSplit := strings.Split(featureRefStr, "/") + featureRefStr = projectSplit[1] } else { - return nil, fmt.Errorf(ErrInvalidFeatureName, featureRef) + return nil, fmt.Errorf(ErrInvalidFeatureRef, featureRefStr) } + } + // parse featureset if specified + if strings.Contains(featureRefStr, ":") { + refSplit := strings.Split(featureRefStr, ":") + featureRef.FeatureSet, featureRefStr = refSplit[0], refSplit[1] + } + featureRef.Name = featureRefStr - if project == "" || name == "" { - return nil, fmt.Errorf(ErrInvalidFeatureName, featureRef) - } + return &featureRef, nil +} - features = append(features, &serving.FeatureReference{ - Name: name, - Project: project, - }) +// Converts a FeatureReference proto into a string +// featureRef - The FeatureReference to render as string +// Returns string representation of the given FeatureReference +func toFeatureRefStr(featureRef *serving.FeatureReference) string { + refStr := "" + // In protov3, unset string and default to "" + if len(featureRef.FeatureSet) > 0 { + refStr += featureRef.FeatureSet + ":" } + refStr += featureRef.Name - return features, nil + return refStr } diff --git a/sdk/go/request_test.go b/sdk/go/request_test.go index df11c1ca45e..70e1f0d8f5e 100644 --- a/sdk/go/request_test.go +++ b/sdk/go/request_test.go @@ -20,35 +20,27 @@ func TestGetOnlineFeaturesRequest(t *testing.T) { { name: "valid", req: OnlineFeaturesRequest{ - Features: []string{"my_project_1/feature1", "my_project_2/feature1", "my_project_4/feature3", "feature2", "feature2"}, + Features: []string{ + "driver:driver_id", + "driver_id", + }, Entities: []Row{ {"entity1": Int64Val(1), "entity2": StrVal("bob")}, {"entity1": Int64Val(1), "entity2": StrVal("annie")}, {"entity1": Int64Val(1), "entity2": StrVal("jane")}, }, - Project: "my_project_3", + Project: "driver_project", }, want: &serving.GetOnlineFeaturesRequest{ Features: []*serving.FeatureReference{ { - Project: "my_project_1", - Name: "feature1", - }, - { - Project: "my_project_2", - Name: "feature1", - }, - { - Project: "my_project_4", - Name: "feature3", - }, - { - Project: "my_project_3", - Name: "feature2", + Project: "driver_project", + FeatureSet: "driver", + Name: "driver_id", }, { - Project: "my_project_3", - Name: "feature2", + Project: "driver_project", + Name: "driver_id", }, }, EntityRows: []*serving.GetOnlineFeaturesRequest_EntityRow{ @@ -76,34 +68,6 @@ func TestGetOnlineFeaturesRequest(t *testing.T) { wantErr: false, err: nil, }, - { - name: "valid_project_in_name", - req: OnlineFeaturesRequest{ - Features: []string{"project/feature1"}, - Entities: []Row{}, - }, - want: &serving.GetOnlineFeaturesRequest{ - Features: []*serving.FeatureReference{ - { - Project: "project", - Name: "feature1", - }, - }, - EntityRows: []*serving.GetOnlineFeaturesRequest_EntityRow{}, - OmitEntitiesInResponse: false, - }, - wantErr: false, - err: nil, - }, - { - name: "no_project", - req: OnlineFeaturesRequest{ - Features: []string{"feature1"}, - Entities: []Row{}, - }, - wantErr: true, - err: fmt.Errorf(ErrInvalidFeatureName, "feature1"), - }, { name: "invalid_feature_name/wrong_format", req: OnlineFeaturesRequest{ @@ -112,7 +76,7 @@ func TestGetOnlineFeaturesRequest(t *testing.T) { Project: "my_project", }, wantErr: true, - err: fmt.Errorf(ErrInvalidFeatureName, "/fs1:feature1"), + err: fmt.Errorf(ErrInvalidFeatureRef, "/fs1:feature1"), }, } for _, tc := range tt { diff --git a/sdk/java/src/main/java/com/gojek/feast/FeastClient.java b/sdk/java/src/main/java/com/gojek/feast/FeastClient.java index c09fe4b922a..c6e7edd0764 100644 --- a/sdk/java/src/main/java/com/gojek/feast/FeastClient.java +++ b/sdk/java/src/main/java/com/gojek/feast/FeastClient.java @@ -23,8 +23,10 @@ import feast.proto.serving.ServingAPIProto.GetOnlineFeaturesRequest.EntityRow; import feast.proto.serving.ServingAPIProto.GetOnlineFeaturesResponse; import feast.proto.serving.ServingServiceGrpc; +import feast.proto.types.ValueProto.Value; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; +import java.util.HashSet; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -56,61 +58,78 @@ public GetFeastServingInfoResponse getFeastServingInfo() { return stub.getFeastServingInfo(GetFeastServingInfoRequest.newBuilder().build()); } + /** + * Get online features from Feast from FeatureSets + * + *

See {@link #getOnlineFeatures(List, List, String, boolean)} + * + * @param featureRefs list of string feature references to retrieve in the following format + * featureSet:feature, where 'featureSet' and 'feature' refer to the FeatureSet and Feature + * names respectively. Only the Feature name is required. + * @param rows list of {@link Row} to select the entities to retrieve the features for. + * @return list of {@link Row} containing retrieved data fields. + */ + public List getOnlineFeatures(List featureRefs, List rows) { + return getOnlineFeatures(featureRefs, rows, ""); + } + /** * Get online features from Feast. * - *

See {@link #getOnlineFeatures(List, List, String)} + *

See {@link #getOnlineFeatures(List, List, String, boolean)} * - * @param features list of string feature references to retrieve, feature reference follows this - * format [project]/[name] + * @param featureRefs list of string feature references to retrieve in the following format + * featureSet:feature, where 'featureSet' and 'feature' refer to the FeatureSet and Feature + * names respectively. Only the Feature name is required. * @param rows list of {@link Row} to select the entities to retrieve the features for - * @param defaultProject {@link String} Default project to find features in if not provided in - * feature reference. - * @return list of {@link Row} containing features + * @param project {@link String} Specifies the project which contains the FeatureSets which the + * Feature requested belong to. + * @return list of {@link Row} containing retrieved data fields. */ - public List getOnlineFeatures(List features, List rows, String defaultProject) { - return getOnlineFeatures(features, rows, defaultProject, false); + public List getOnlineFeatures(List featureRefs, List rows, String project) { + return getOnlineFeatures(featureRefs, rows, project, false); } /** * Get online features from Feast. * - *

Example of retrieving online features for the driver project, with features driver_id and + *

Example of retrieving online features for the driver featureset, with features driver_id and * driver_name * *

{@code
    * FeastClient client = FeastClient.create("localhost", 6566);
-   * List requestedFeatureIds = Arrays.asList("driver/driver_id", "driver/driver_name");
+   * List requestedFeatureIds = Arrays.asList("driver:driver_id", "driver:driver_name");
    * List requestedRows =
    *         Arrays.asList(Row.create().set("driver_id", 123), Row.create().set("driver_id", 456));
    * List retrievedFeatures = client.getOnlineFeatures(requestedFeatureIds, requestedRows);
    * retrievedFeatures.forEach(System.out::println);
    * }
* - * @param featureRefStrings list of feature refs to retrieve, feature refs follow this format - * [project]/[name] + * @param featureRefs list of string feature references to retrieve in the following format + * featureSet:feature, where 'featureSet' and 'feature' refer to the FeatureSet and Feature + * names respectively. Only the Feature name is required. * @param rows list of {@link Row} to select the entities to retrieve the features for - * @param defaultProject {@link String} Default project to find features in if not provided in - * feature reference. + * @param project {@link String} Specifies the project which contains the FeatureSets which the + * Feature requested belong to. * @param omitEntitiesInResponse if true, the returned {@link Row} will not contain field and * value for the entity - * @return list of {@link Row} containing features + * @return list of {@link Row} containing retrieved data fields. */ public List getOnlineFeatures( - List featureRefStrings, - List rows, - String defaultProject, - boolean omitEntitiesInResponse) { - List features = - RequestUtil.createFeatureRefs(featureRefStrings, defaultProject); + List featureRefs, List rows, String project, boolean omitEntitiesInResponse) { + List features = RequestUtil.createFeatureRefs(featureRefs, project); + // build entity rows and collect entity references + HashSet entityRefs = new HashSet<>(); List entityRows = rows.stream() .map( - row -> - EntityRow.newBuilder() - .setEntityTimestamp(row.getEntityTimestamp()) - .putAllFields(row.getFields()) - .build()) + row -> { + entityRefs.addAll(row.getFields().keySet()); + return EntityRow.newBuilder() + .setEntityTimestamp(row.getEntityTimestamp()) + .putAllFields(row.getFields()) + .build(); + }) .collect(Collectors.toList()); GetOnlineFeaturesResponse response = @@ -125,7 +144,18 @@ public List getOnlineFeatures( .map( field -> { Row row = Row.create(); - field.getFieldsMap().forEach(row::set); + field + .getFieldsMap() + .forEach( + (String name, Value value) -> { + // Strip project from string Feature References from returned from serving + if (!entityRefs.contains(name)) { + FeatureReference featureRef = + RequestUtil.parseFeatureRef(name, true).build(); + name = RequestUtil.renderFeatureRef(featureRef); + } + row.set(name, value); + }); return row; }) .collect(Collectors.toList()); diff --git a/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java b/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java index 3505f646c2b..3a1a0919d3e 100644 --- a/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java +++ b/sdk/java/src/main/java/com/gojek/feast/RequestUtil.java @@ -17,47 +17,89 @@ package com.gojek.feast; import feast.proto.serving.ServingAPIProto.FeatureReference; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @SuppressWarnings("WeakerAccess") public class RequestUtil { + /** + * Create feature references protos from given string feature reference. + * + * @param featureRefStrings to create Feature Reference protos from + * @param project specifies to the project set in parsed Feature Reference protos otherwise "" + * @return List of parsed {@link FeatureReference} protos + */ public static List createFeatureRefs( - List featureRefStrings, String defaultProject) { + List featureRefStrings, String project) { if (featureRefStrings == null) { throw new IllegalArgumentException("featureRefs cannot be null"); } - List featureRefs = new ArrayList<>(); + List featureRefs = + featureRefStrings.stream() + .map(refStr -> parseFeatureRef(refStr, false)) + .collect(Collectors.toList()); + // apply project override if specified + if (!project.isEmpty()) { + featureRefs = + featureRefs.stream().map(ref -> ref.setProject(project)).collect(Collectors.toList()); + } - for (String featureRefString : featureRefStrings) { - String project; - String name; - String[] projectSplit = featureRefString.split("/"); + return featureRefs.stream().map(ref -> ref.build()).collect(Collectors.toList()); + } - if (projectSplit.length == 1) { - project = defaultProject; - name = projectSplit[0]; - } else if (projectSplit.length == 2) { - project = projectSplit[0]; - name = projectSplit[1]; + /** + * Parse a feature reference proto builder from the given featureRefString + * + * @param featureRefString string feature reference to parse from. + * @param ignoreProject If true, would ignore if project is specified in given ref string. + * Otherwise, throwws a {@link IllegalArgumentException} if project is specified. + * @return a parsed {@link FeatureReference.Builder} + */ + public static FeatureReference.Builder parseFeatureRef( + String featureRefString, boolean ignoreProject) { + featureRefString = featureRefString.trim(); + if (featureRefString.isEmpty()) { + throw new IllegalArgumentException("Cannot parse a empty feature reference"); + } + FeatureReference.Builder featureRef = FeatureReference.newBuilder(); + + // parse project if specified + if (featureRefString.contains("/")) { + if (ignoreProject) { + String[] projectSplit = featureRefString.split("/"); + featureRefString = projectSplit[1]; } else { throw new IllegalArgumentException( - String.format( - "Feature id '%s' has invalid format. Expected format: /.", - featureRefString)); + String.format("Unsupported feature reference: %s", featureRefString)); } + } - if (project.isEmpty() || name.isEmpty() || name.contains(":")) { - throw new IllegalArgumentException( - String.format( - "Feature id '%s' has invalid format. Expected format: /.", - featureRefString)); - } + // parse featureset if specified + if (featureRefString.contains(":")) { + String[] featureSetSplit = featureRefString.split(":"); + featureRef.setFeatureSet(featureSetSplit[0]); + featureRefString = featureSetSplit[1]; + } + featureRef.setName(featureRefString); + return featureRef; + } - featureRefs.add(FeatureReference.newBuilder().setName(name).setProject(project).build()); + /** + * Render a feature reference as string. + * + * @param featureReference to render as string + * @return string represenation of feature reference. + */ + public static String renderFeatureRef(FeatureReference featureReference) { + String refStr = ""; + // In protov3, unset string and int fields default to "" and 0 respectively + if (!featureReference.getFeatureSet().isEmpty()) { + refStr += featureReference.getFeatureSet() + ":"; } - return featureRefs; + refStr = refStr + featureReference.getName(); + + return refStr; } } diff --git a/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java b/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java index c066ffa0b30..f10d82b8726 100644 --- a/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java +++ b/sdk/java/src/test/java/com/gojek/feast/RequestUtilTest.java @@ -22,9 +22,9 @@ import com.google.protobuf.TextFormat; import feast.proto.serving.ServingAPIProto.FeatureReference; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -33,51 +33,27 @@ class RequestUtilTest { - private static Stream provideValidFeatureIds() { + private static Stream provideValidFeatureRefs() { return Stream.of( Arguments.of( - Collections.singletonList("driver_project/driver_id"), - Collections.singletonList( - FeatureReference.newBuilder() - .setProject("driver_project") - .setName("driver_id") - .build())), - Arguments.of( - Arrays.asList("driver_project/driver_id", "driver_project/driver_name"), + Arrays.asList("driver:driver_id", "driver_id"), Arrays.asList( FeatureReference.newBuilder() .setProject("driver_project") + .setFeatureSet("driver") .setName("driver_id") .build(), - FeatureReference.newBuilder() - .setProject("driver_project") - .setName("driver_name") - .build())), - Arguments.of( - Arrays.asList( - "driver_project/driver_id", - "driver_project/driver_name", - "booking_project/driver_name"), - Arrays.asList( FeatureReference.newBuilder() .setProject("driver_project") .setName("driver_id") - .build(), - FeatureReference.newBuilder() - .setProject("driver_project") - .setName("driver_name") - .build(), - FeatureReference.newBuilder() - .setProject("booking_project") - .setName("driver_name") .build()))); } @ParameterizedTest - @MethodSource("provideValidFeatureIds") - void createFeatureSets_ShouldReturnFeatureSetsForValidFeatureIds( + @MethodSource("provideValidFeatureRefs") + void createFeatureSets_ShouldReturnFeatureSetsForValidFeatureRefs( List input, List expected) { - List actual = RequestUtil.createFeatureRefs(input, "my-project"); + List actual = RequestUtil.createFeatureRefs(input, "driver_project"); // Order of the actual and expected featureSets do no not matter actual.sort(Comparator.comparing(FeatureReference::getName)); expected.sort(Comparator.comparing(FeatureReference::getName)); @@ -89,23 +65,35 @@ void createFeatureSets_ShouldReturnFeatureSetsForValidFeatureIds( } } + @ParameterizedTest + @MethodSource("provideValidFeatureRefs") + void renderFeatureRef_ShouldReturnFeatureRefString( + List expected, List input) { + input = + input.stream() + .map(ref -> ref.toBuilder().clearProject().build()) + .collect(Collectors.toList()); + List actual = + input.stream().map(ref -> RequestUtil.renderFeatureRef(ref)).collect(Collectors.toList()); + assertEquals(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) { + assertEquals(expected.get(i), actual.get(i)); + } + } + private static Stream provideInvalidFeatureRefs() { - return Stream.of( - Arguments.of(Collections.singletonList("/noproject")), - Arguments.of(Collections.singletonList(""))); + return Stream.of(Arguments.of(List.of("project/feature", ""))); } @ParameterizedTest @MethodSource("provideInvalidFeatureRefs") void createFeatureSets_ShouldThrowExceptionForInvalidFeatureRefs(List input) { - assertThrows( - IllegalArgumentException.class, () -> RequestUtil.createFeatureRefs(input, "my-project")); + assertThrows(IllegalArgumentException.class, () -> RequestUtil.createFeatureRefs(input, "")); } @ParameterizedTest @NullSource void createFeatureSets_ShouldThrowExceptionForNullFeatureRefs(List input) { - assertThrows( - IllegalArgumentException.class, () -> RequestUtil.createFeatureRefs(input, "my-project")); + assertThrows(IllegalArgumentException.class, () -> RequestUtil.createFeatureRefs(input, "")); } } diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py index 2fd5a4cdf56..87f12301f86 100644 --- a/sdk/python/feast/cli.py +++ b/sdk/python/feast/cli.py @@ -137,6 +137,7 @@ def feature_set_list(): @feature_set.command("apply") +# TODO: add project option to overwrite project setting. @click.option( "--filename", "-f", diff --git a/sdk/python/feast/client.py b/sdk/python/feast/client.py index 89a89fd2a8d..50e3a65bad2 100644 --- a/sdk/python/feast/client.py +++ b/sdk/python/feast/client.py @@ -36,6 +36,7 @@ CONFIG_PROJECT_KEY, CONFIG_SERVING_SECURE_KEY, CONFIG_SERVING_URL_KEY, + FEAST_DEFAULT_OPTIONS, ) from feast.core.CoreService_pb2 import ( ApplyFeatureSetRequest, @@ -57,6 +58,7 @@ ) from feast.core.CoreService_pb2_grpc import CoreServiceStub from feast.core.FeatureSet_pb2 import FeatureSetStatus +from feast.feature import FeatureRef from feast.feature_set import Entity, FeatureSet, FeatureSetRef from feast.job import IngestJob, RetrievalJob from feast.loaders.abstract_producer import get_producer @@ -290,13 +292,15 @@ def project(self) -> Union[str, None]: """ return self._config.get(CONFIG_PROJECT_KEY) - def set_project(self, project: str): + def set_project(self, project: Optional[str] = None): """ Set currently active Feast project Args: - project: Project to set as active + project: Project to set as active. If unset, will reset to the default project. """ + if project is None: + project = FEAST_DEFAULT_OPTIONS[CONFIG_PROJECT_KEY] self._config.set(CONFIG_PROJECT_KEY, project) def list_projects(self) -> List[str]: @@ -339,13 +343,17 @@ def archive_project(self, project): """ self._connect_core() - self._core_service_stub.ArchiveProject( - ArchiveProjectRequest(name=project), - timeout=self._config.getint(CONFIG_GRPC_CONNECTION_TIMEOUT_DEFAULT_KEY), - ) # type: ArchiveProjectResponse + try: + self._core_service_stub.ArchiveProject( + ArchiveProjectRequest(name=project), + timeout=self._config.getint(CONFIG_GRPC_CONNECTION_TIMEOUT_DEFAULT_KEY), + ) # type: ArchiveProjectResponse + except grpc.RpcError as e: + raise grpc.RpcError(e.details()) + # revert to the default project if self._project == project: - self._project = "" + self._project = FEAST_DEFAULT_OPTIONS[CONFIG_PROJECT_KEY] def apply(self, feature_sets: Union[List[FeatureSet], FeatureSet]): """ @@ -498,23 +506,24 @@ def get_batch_features( self, feature_refs: List[str], entity_rows: Union[pd.DataFrame, str], - default_project: str = None, + project: str = None, ) -> RetrievalJob: """ Retrieves historical features from a Feast Serving deployment. Args: - feature_refs (List[str]): - List of feature references that will be returned for each entity. - Each feature reference should have the following format - "project/feature". - + feature_refs: List of feature references that will be returned for each entity. + Each feature reference should have the following format: + "feature_set:feature" where "feature_set" & "feature" refer to + the feature and feature set names respectively. + Only the feature name is required. entity_rows (Union[pd.DataFrame, str]): Pandas dataframe containing entities and a 'datetime' column. Each entity in a feature set must be present as a column in this dataframe. The datetime column must contain timestamps in datetime64 format. - default_project: Default project where feature values will be found. + project: Specifies the project which contain the FeatureSets + which the requested features belong to. Returns: feast.job.RetrievalJob: @@ -542,10 +551,6 @@ def get_batch_features( self._connect_serving() - feature_references = _build_feature_references( - feature_refs=feature_refs, default_project=default_project - ) - # Retrieve serving information to determine store type and # staging location serving_info = self._serving_service_stub.GetFeastServingInfo( @@ -587,7 +592,10 @@ def get_batch_features( entity_rows, serving_info.job_staging_location ) # type: List[str] request = GetBatchFeaturesRequest( - features=feature_references, + features=_build_feature_references( + feature_ref_strs=feature_refs, + project=project if project is not None else self.project, + ), dataset_source=DatasetSource( file_source=DatasetSource.FileSource( file_uris=staged_files, data_format=DataFormat.DATA_FORMAT_AVRO @@ -607,23 +615,22 @@ def get_online_features( self, feature_refs: List[str], entity_rows: List[GetOnlineFeaturesRequest.EntityRow], - default_project: Optional[str] = None, + project: Optional[str] = None, ) -> GetOnlineFeaturesResponse: """ Retrieves the latest online feature data from Feast Serving Args: - feature_refs: List of feature references in the following format - [project]/[feature_name]. Only the feature name - is a required component in the reference. - example: - ["my_project/my_feature_1", - "my_feature_4",] + feature_refs: List of feature references that will be returned for each entity. + Each feature reference should have the following format: + "feature_set:feature" where "feature_set" & "feature" refer to + the feature and feature set names respectively. + Only the feature name is required. entity_rows: List of GetFeaturesRequest.EntityRow where each row contains entities. Timestamp should not be set for online retrieval. All entity types within a feature - default_project: This project will be used if the project name is - not provided in the feature reference + project: Specifies the project which contain the FeatureSets + which the requested features belong to. Returns: Returns a list of maps where each item in the list contains the @@ -635,14 +642,37 @@ def get_online_features( response = self._serving_service_stub.GetOnlineFeatures( GetOnlineFeaturesRequest( features=_build_feature_references( - feature_refs=feature_refs, - default_project=( - default_project if not self.project else self.project - ), + feature_ref_strs=feature_refs, + project=project if project is not None else self.project, ), entity_rows=entity_rows, ) ) + # collect entity row refs + entity_refs = set() + for entity_row in entity_rows: + entity_refs.update(entity_row.fields.keys()) + + strip_field_values = [] + for field_value in response.field_values: + # strip the project part the string feature references returned from serving + strip_fields = {} + for ref_str, value in field_value.fields.items(): + # find and ignore entities + if ref_str in entity_refs: + strip_fields[ref_str] = value + else: + strip_ref_str = repr( + FeatureRef.from_str(ref_str, ignore_project=True) + ) + strip_fields[strip_ref_str] = value + strip_field_values.append( + GetOnlineFeaturesResponse.FieldValues(fields=strip_fields) + ) + + del response.field_values[:] + response.field_values.extend(strip_field_values) + except grpc.RpcError as e: raise grpc.RpcError(e.details()) @@ -834,50 +864,25 @@ def ingest( def _build_feature_references( - feature_refs: List[str], default_project: str = None + feature_ref_strs: List[str], project: Optional[str] = None ) -> List[FeatureReference]: """ - Builds a list of FeatureSet objects from feature set ids in order to - retrieve feature data from Feast Serving + Builds a list of FeatureReference protos from string feature set references Args: - feature_refs: List of feature reference strings - ("project/feature") - default_project: This project will be used if the project name is - not provided in the feature reference - """ - - features = [] + feature_ref_strs: List of string feature references + project: Optionally specifies the project in the parsed feature references. - for feature_ref in feature_refs: - project_split = feature_ref.split("/") - - if len(project_split) == 2: - project, name = project_split - elif len(project_split) == 1: - name = project_split[0] - if default_project is None: - raise ValueError( - f"No project specified in {feature_ref} and no default project provided" - ) - project = default_project - else: - raise ValueError( - f'Could not parse feature ref {feature_ref}, expecting "project/feature"' - ) - - if len(project) == 0 or len(name) == 0: - raise ValueError( - f'Could not parse feature ref {feature_ref}, expecting "project/feature"' - ) - - if ":" in name: - raise ValueError( - f'Could not parse feature ref {feature_ref}, expecting "project/feature". Versions were deprecated in v0.5.0.' - ) - - features.append(FeatureReference(project=project, name=name)) - return features + Returns: + A list of FeatureReference protos parsed from args. + """ + feature_refs = [FeatureRef.from_str(ref_str) for ref_str in feature_ref_strs] + feature_ref_protos = [ref.to_proto() for ref in feature_refs] + # apply project if specified + if project is not None: + for feature_ref_proto in feature_ref_protos: + feature_ref_proto.project = project + return feature_ref_protos def _generate_ingestion_id(feature_set: FeatureSet) -> str: diff --git a/sdk/python/feast/feature.py b/sdk/python/feast/feature.py index f5c07070b09..35e0ea32aeb 100644 --- a/sdk/python/feast/feature.py +++ b/sdk/python/feast/feature.py @@ -14,6 +14,7 @@ from feast.core.FeatureSet_pb2 import FeatureSpec as FeatureProto from feast.field import Field +from feast.serving.ServingService_pb2 import FeatureReference as FeatureRefProto from feast.types import Value_pb2 as ValueTypeProto from feast.value_type import ValueType @@ -62,3 +63,86 @@ def from_proto(cls, feature_proto: FeatureProto): feature.update_shape_type(feature_proto) feature.update_domain_info(feature_proto) return feature + + +class FeatureRef: + """ Feature Reference represents a reference to a specific feature. """ + + def __init__(self, name: str, feature_set: str = None): + self.proto = FeatureRefProto(name=name, feature_set=feature_set) + + @classmethod + def from_proto(cls, proto: FeatureRefProto): + """ + Construct a feature reference from the given FeatureReference proto + + Arg: + proto: Protobuf FeatureReference to construct from + + Returns: + FeatureRef that refers to the given feature + """ + return cls(name=proto.name, feature_set=proto.feature_set) + + @classmethod + def from_str(cls, feature_ref_str: str, ignore_project: bool = False): + """ + Parse the given string feature reference into FeatureRef model + String feature reference should be in the format feature_set:feature. + Where "feature_set" and "name" are the feature_set name and feature name + respectively. + + Args: + feature_ref_str: String representation of the feature reference + ignore_project: Ignore projects in given string feature reference + instead throwing an error + + Returns: + FeatureRef that refers to the given feature + """ + proto = FeatureRefProto() + if "/" in feature_ref_str: + if ignore_project: + _, feature_ref_str = feature_ref_str.split("/") + else: + raise ValueError(f"Unsupported feature reference: {feature_ref_str}") + + # parse feature set name if specified + if ":" in feature_ref_str: + proto.feature_set, feature_ref_str = feature_ref_str.split(":") + + proto.name = feature_ref_str + return cls.from_proto(proto) + + def to_proto(self) -> FeatureRefProto: + """ + Convert and return this feature set reference to protobuf. + + Returns: + Protobuf respresentation of this feature set reference. + """ + return self.proto + + def __repr__(self): + # return string representation of the reference + # [project/][feature_set:]name + # in protov3 unset string and int fields default to "" and 0 + ref_str = "" + if len(self.proto.project) > 0: + ref_str += self.proto.project + "/" + if len(self.proto.feature_set) > 0: + ref_str += self.proto.feature_set + ":" + ref_str += self.proto.name + return ref_str + + def __str__(self): + # human readable string of the reference + return f"FeatureRef<{self.__repr__()}>" + + def __eq__(self, other): + # compare with other feature set + return hash(self) == hash(other) + + def __hash__(self): + # hash this reference + return hash(repr(self)) diff --git a/sdk/python/feast/feature_set.py b/sdk/python/feast/feature_set.py index 3c77aa1db5e..aebee52ca42 100644 --- a/sdk/python/feast/feature_set.py +++ b/sdk/python/feast/feature_set.py @@ -898,11 +898,11 @@ def __str__(self): def __repr__(self): # return string representation of the reference # [project/]name + # in protov3 unset string and int fields default to "" and 0 ref_str = "" - if self.proto.project: + if len(self.proto.project) > 0: ref_str += self.proto.project + "/" - if self.proto.name: - ref_str += self.proto.name + ref_str += self.proto.name return ref_str def __eq__(self, other): diff --git a/sdk/python/feast/job.py b/sdk/python/feast/job.py index f2e31c709e2..21b08224bad 100644 --- a/sdk/python/feast/job.py +++ b/sdk/python/feast/job.py @@ -37,7 +37,9 @@ class RetrievalJob: A class representing a job for feature retrieval in Feast. """ - def __init__(self, job_proto: JobProto, serving_stub: ServingServiceStub): + def __init__( + self, job_proto: JobProto, serving_stub: ServingServiceStub, + ): """ Args: job_proto: Job proto object (wrapped by this job object) @@ -45,7 +47,8 @@ def __init__(self, job_proto: JobProto, serving_stub: ServingServiceStub): """ self.job_proto = job_proto self.serving_stub = serving_stub - self.storage_client = storage.Client(project=None) + # TODO: abstract away GCP depedency + self.gcs_client = storage.Client(project=None) @property def id(self): @@ -125,7 +128,7 @@ def result(self, timeout_sec: int = DEFAULT_TIMEOUT_SEC): for file_uri in uris: if file_uri.scheme == "gs": file_obj = tempfile.TemporaryFile() - self.storage_client.download_blob_to_file(file_uri.geturl(), file_obj) + self.gcs_client.download_blob_to_file(file_uri.geturl(), file_obj) elif file_uri.scheme == "file": file_obj = open(file_uri.path, "rb") else: diff --git a/sdk/python/feast/test_feature.py b/sdk/python/feast/test_feature.py new file mode 100644 index 00000000000..a4f0ff58ff2 --- /dev/null +++ b/sdk/python/feast/test_feature.py @@ -0,0 +1,23 @@ +# Copyright 2019 The Feast Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from feast.feature import FeatureRef + + +class TestFeatureRef: + def test_str_ref(self): + original_ref = FeatureRef(project="test", name="test") + ref_str = repr(original_ref) + parsed_ref = FeatureRef.from_str(ref_str) + assert original_ref == parsed_ref diff --git a/sdk/python/tests/test_client.py b/sdk/python/tests/test_client.py index f143f18c8f2..380557ce928 100644 --- a/sdk/python/tests/test_client.py +++ b/sdk/python/tests/test_client.py @@ -14,13 +14,18 @@ import pkgutil +import tempfile from concurrent import futures +from datetime import datetime from unittest import mock import grpc +import pandas as pd +import pandavro import pytest from google.protobuf.duration_pb2 import Duration from mock import MagicMock, patch +from pytz import timezone import dataframes import feast.core.CoreService_pb2_grpc as Core @@ -45,10 +50,16 @@ from feast.feature_set import Feature, FeatureSet, FeatureSetRef from feast.job import IngestJob from feast.serving.ServingService_pb2 import ( + DataFormat, + FeastServingType, + GetBatchFeaturesResponse, GetFeastServingInfoResponse, + GetJobResponse, GetOnlineFeaturesRequest, GetOnlineFeaturesResponse, ) +from feast.serving.ServingService_pb2 import Job as BatchRetrievalJob +from feast.serving.ServingService_pb2 import JobStatus, JobType from feast.source import KafkaSource from feast.types import Value_pb2 as ValueProto from feast.value_type import ValueType @@ -196,12 +207,17 @@ def test_get_online_features(self, mocked_client, mocker): grpc.insecure_channel("") ) - fields = dict() - for feature_num in range(1, 10): - fields[f"my_project/feature_{str(feature_num)}"] = ValueProto.Value( - int64_val=feature_num - ) - field_values = GetOnlineFeaturesResponse.FieldValues(fields=fields) + def int_val(x): + return ValueProto.Value(int64_val=x) + + # serving can return feature references with projects, + # get_online_features() should strip the project part. + field_values = GetOnlineFeaturesResponse.FieldValues( + fields={ + "driver_project/driver:driver_id": int_val(1), + "driver_project/driver_id": int_val(9), + } + ) response = GetOnlineFeaturesResponse() entity_rows = [] @@ -209,7 +225,7 @@ def test_get_online_features(self, mocked_client, mocker): response.field_values.append(field_values) entity_rows.append( GetOnlineFeaturesRequest.EntityRow( - fields={"customer_id": ValueProto.Value(int64_val=row_number)} + fields={"customer_id": int_val(row_number)} ) ) @@ -219,24 +235,17 @@ def test_get_online_features(self, mocked_client, mocker): return_value=response, ) + # NOTE: Feast Serving does not allow for feature references + # that specify the same feature in the same request response = mocked_client.get_online_features( entity_rows=entity_rows, - feature_refs=[ - "my_project/feature_1", - "my_project/feature_2", - "my_project/feature_3", - "my_project/feature_4", - "my_project/feature_5", - "my_project/feature_6", - "my_project/feature_7", - "my_project/feature_8", - "my_project/feature_9", - ], + feature_refs=["driver:driver_id", "driver_id"], + project="driver_project", ) # type: GetOnlineFeaturesResponse assert ( - response.field_values[0].fields["my_project/feature_1"].int64_val == 1 - and response.field_values[0].fields["my_project/feature_9"].int64_val == 9 + response.field_values[0].fields["driver:driver_id"].int64_val == 1 + and response.field_values[0].fields["driver_id"].int64_val == 9 ) @pytest.mark.parametrize( @@ -400,130 +409,125 @@ def test_stop_ingest_job(self, mocked_client, mocker): mocked_client.stop_ingest_job(ingest_job) assert mocked_client._core_service_stub.StopIngestionJob.called - # @pytest.mark.parametrize - # "mocked_client", - # [pytest.lazy_fixture("mock_client"), pytest.lazy_fixture("secure_mock_client")], - # ) - # def test_get_batch_features(self, mocked_client, mocker): - # - # mocked_client._serving_service_stub = Serving.ServingServiceStub( - # grpc.insecure_channel("") - # ) - # mocked_client._core_service_stub = Core.CoreServiceStub( - # grpc.insecure_channel("") - # ) - # - # mocker.patch.object( - # mocked_client._core_service_stub, - # "GetFeatureSet", - # return_value=GetFeatureSetResponse( - # feature_set=FeatureSetProto( - # spec=FeatureSetSpecProto( - # name="customer_fs", - # project="my_project", - # entities=[ - # EntitySpecProto( - # name="customer", value_type=ValueProto.ValueType.INT64 - # ), - # EntitySpecProto( - # name="transaction", - # value_type=ValueProto.ValueType.INT64, - # ), - # ], - # features=[ - # FeatureSpecProto( - # name="customer_feature_1", - # value_type=ValueProto.ValueType.FLOAT, - # ), - # FeatureSpecProto( - # name="customer_feature_2", - # value_type=ValueProto.ValueType.STRING, - # ), - # ], - # ), - # meta=FeatureSetMetaProto(status=FeatureSetStatusProto.STATUS_READY), - # ) - # ), - # ) - # - # expected_dataframe = pd.DataFrame( - # { - # "datetime": [datetime.utcnow() for _ in range(3)], - # "customer": [1001, 1002, 1003], - # "transaction": [1001, 1002, 1003], - # "my_project/customer_feature_1": [1001, 1002, 1003], - # "my_project/customer_feature_2": [1001, 1002, 1003], - # } - # ) - # - # final_results = tempfile.mktemp() - # to_avro(file_path_or_buffer=final_results, df=expected_dataframe) - # - # mocker.patch.object( - # mocked_client._serving_service_stub, - # "GetBatchFeatures", - # return_value=GetBatchFeaturesResponse( - # job=BatchFeaturesJob( - # id="123", - # type=JobType.JOB_TYPE_DOWNLOAD, - # status=JobStatus.JOB_STATUS_DONE, - # file_uris=[f"file://{final_results}"], - # data_format=DataFormat.DATA_FORMAT_AVRO, - # ) - # ), - # ) - # - # mocker.patch.object( - # mocked_client._serving_service_stub, - # "GetJob", - # return_value=GetJobResponse( - # job=BatchFeaturesJob( - # id="123", - # type=JobType.JOB_TYPE_DOWNLOAD, - # status=JobStatus.JOB_STATUS_DONE, - # file_uris=[f"file://{final_results}"], - # data_format=DataFormat.DATA_FORMAT_AVRO, - # ) - # ), - # ) - # - # mocker.patch.object( - # mocked_client._serving_service_stub, - # "GetFeastServingInfo", - # return_value=GetFeastServingInfoResponse( - # job_staging_location=f"file://{tempfile.mkdtemp()}/", - # type=FeastServingType.FEAST_SERVING_TYPE_BATCH, - # ), - # ) - # - # mocked_client.set_project("project1") - # response = mocked_client.get_batch_features( - # entity_rows=pd.DataFrame( - # { - # "datetime": [ - # pd.datetime.now(tz=timezone("Asia/Singapore")) for _ in range(3) - # ], - # "customer": [1001, 1002, 1003], - # "transaction": [1001, 1002, 1003], - # } - # ), - # feature_refs=[ - # "my_project/customer_feature_1", - # "my_project/customer_feature_2", - # ], - # ) # type: Job - # - # assert response.id == "123" and response.status == JobStatus.JOB_STATUS_DONE - # - # actual_dataframe = response.to_dataframe() - # - # assert actual_dataframe[ - # ["my_project/customer_feature_1", "my_project/customer_feature_2"] - # ].equals( - # expected_dataframe[ - # ["my_project/customer_feature_1", "my_project/customer_feature_2"] - # ] - # ) + @pytest.mark.parametrize( + "mocked_client", + [pytest.lazy_fixture("mock_client"), pytest.lazy_fixture("secure_mock_client")], + ) + def test_get_batch_features(self, mocked_client, mocker): + + mocked_client._serving_service_stub = Serving.ServingServiceStub( + grpc.insecure_channel("") + ) + mocked_client._core_service_stub = Core.CoreServiceStub( + grpc.insecure_channel("") + ) + + mocker.patch.object( + mocked_client._core_service_stub, + "GetFeatureSet", + return_value=GetFeatureSetResponse( + feature_set=FeatureSetProto( + spec=FeatureSetSpecProto( + name="driver", + project="driver_project", + entities=[ + EntitySpecProto( + name="driver", value_type=ValueProto.ValueType.INT64 + ), + EntitySpecProto( + name="transaction", + value_type=ValueProto.ValueType.INT64, + ), + ], + features=[ + FeatureSpecProto( + name="driver_id", value_type=ValueProto.ValueType.FLOAT, + ), + FeatureSpecProto( + name="driver_name", + value_type=ValueProto.ValueType.STRING, + ), + ], + ), + meta=FeatureSetMetaProto(status=FeatureSetStatusProto.STATUS_READY), + ) + ), + ) + + expected_dataframe = pd.DataFrame( + { + "datetime": [datetime.utcnow() for _ in range(3)], + "driver": [1001, 1002, 1003], + "transaction": [1001, 1002, 1003], + "driver_id": [1001, 1002, 1003], + } + ) + + final_results = tempfile.mktemp() + pandavro.to_avro(file_path_or_buffer=final_results, df=expected_dataframe) + + mocker.patch.object( + mocked_client._serving_service_stub, + "GetBatchFeatures", + return_value=GetBatchFeaturesResponse( + job=BatchRetrievalJob( + id="123", + type=JobType.JOB_TYPE_DOWNLOAD, + status=JobStatus.JOB_STATUS_DONE, + file_uris=[f"file://{final_results}"], + data_format=DataFormat.DATA_FORMAT_AVRO, + ) + ), + ) + + mocker.patch.object( + mocked_client._serving_service_stub, + "GetJob", + return_value=GetJobResponse( + job=BatchRetrievalJob( + id="123", + type=JobType.JOB_TYPE_DOWNLOAD, + status=JobStatus.JOB_STATUS_DONE, + file_uris=[f"file://{final_results}"], + data_format=DataFormat.DATA_FORMAT_AVRO, + ) + ), + ) + + mocker.patch.object( + mocked_client._serving_service_stub, + "GetFeastServingInfo", + return_value=GetFeastServingInfoResponse( + job_staging_location=f"file://{tempfile.mkdtemp()}/", + type=FeastServingType.FEAST_SERVING_TYPE_BATCH, + ), + ) + + mocked_client.set_project("project1") + # TODO: Abstract away GCS client and GCP dependency + # NOTE: Feast Serving does not allow for feature references + # that specify the same feature in the same request. + with patch("google.cloud.storage.Client"): + response = mocked_client.get_batch_features( + entity_rows=pd.DataFrame( + { + "datetime": [ + pd.datetime.now(tz=timezone("Asia/Singapore")) + for _ in range(3) + ], + "driver": [1001, 1002, 1003], + "transaction": [1001, 1002, 1003], + } + ), + feature_refs=["driver:driver_id", "driver_id"], + project="driver_project", + ) # Type: GetBatchFeaturesResponse + + assert response.id == "123" and response.status == JobStatus.JOB_STATUS_DONE + + actual_dataframe = response.to_dataframe() + + assert actual_dataframe[["driver_id"]].equals(expected_dataframe[["driver_id"]]) @pytest.mark.parametrize( "test_client", diff --git a/serving/src/main/java/feast/serving/specs/CachedSpecService.java b/serving/src/main/java/feast/serving/specs/CachedSpecService.java index edb8ee6ee55..07b0b8bbbd2 100644 --- a/serving/src/main/java/feast/serving/specs/CachedSpecService.java +++ b/serving/src/main/java/feast/serving/specs/CachedSpecService.java @@ -38,6 +38,7 @@ import io.prometheus.client.Gauge; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -51,12 +52,15 @@ public class CachedSpecService { private static final int MAX_SPEC_COUNT = 1000; private static final Logger log = org.slf4j.LoggerFactory.getLogger(CachedSpecService.class); + private static final String DEFAULT_PROJECT_NAME = "default"; + // flag to signal that multiple featuresets match a specific + // string feature reference in the feature to featureset mapping. + private static final String FEATURE_SET_CONFLICT_FLAG = "##CONFLICT##"; private final CoreSpecService coreService; - private final Map featureToFeatureSetMapping; + private Map featureToFeatureSetMapping; - private final CacheLoader featureSetCacheLoader; private final LoadingCache featureSetCache; private Store store; @@ -80,7 +84,7 @@ public CachedSpecService(CoreSpecService coreService, StoreProto.Store store) { Map featureSets = getFeatureSetMap(); featureToFeatureSetMapping = new ConcurrentHashMap<>(getFeatureToFeatureSetMapping(featureSets)); - featureSetCacheLoader = CacheLoader.from(featureSets::get); + CacheLoader featureSetCacheLoader = CacheLoader.from(featureSets::get); featureSetCache = CacheBuilder.newBuilder().maximumSize(MAX_SPEC_COUNT).build(featureSetCacheLoader); featureSetCache.putAll(featureSets); @@ -100,7 +104,9 @@ public FeatureSetSpec getFeatureSetSpec(String featureSetRef) throws ExecutionEx } /** - * Get FeatureSetSpecs for the given features. + * Get FeatureSetSpecs for the given features references. If the project is unspecified in the + * given references, autofills the default project. Throws a {@link SpecRetrievalException}. If + * multiple feature sets match given string reference, * * @return FeatureSetRequest containing the specs, and their respective feature references */ @@ -109,15 +115,22 @@ public List getFeatureSets(List featureRefe featureReferences.stream() .map( featureReference -> { - String featureSet = + // map feature reference to coresponding feature set name + String fsName = featureToFeatureSetMapping.get(generateFeatureStringRef(featureReference)); - if (featureSet == null) { + if (fsName == null) { throw new SpecRetrievalException( String.format( - "Unable to find feature set for feature ref: " + "(project: %s, name: %s)", - featureReference.getProject(), featureReference.getName())); + "Unable to find Feature Set for the given Feature Reference: %s", + generateFeatureStringRef(featureReference))); + } else if (fsName == FEATURE_SET_CONFLICT_FLAG) { + throw new SpecRetrievalException( + String.format( + "Given Feature Reference is amibigous as it matches multiple Feature Sets: %s." + + "Please specify a more specific Feature Reference (ie specify the project or feature set)", + generateFeatureStringRef(featureReference))); } - return Pair.of(featureSet, featureReference); + return Pair.of(fsName, featureReference); }) .collect(groupingBy(Pair::getLeft)) .forEach( @@ -126,6 +139,19 @@ public List getFeatureSets(List featureRefe FeatureSetSpec featureSetSpec = featureSetCache.get(fsName); List requestedFeatures = featureRefs.stream().map(Pair::getRight).collect(Collectors.toList()); + + // check that requested features reference point to different features in the + // featureset. + HashSet featureNames = new HashSet<>(); + requestedFeatures.forEach( + ref -> { + if (featureNames.contains(ref.getName())) { + throw new SpecRetrievalException( + "Multiple Feature References referencing the same feature in a featureset is not allowed."); + } + featureNames.add(ref.getName()); + }); + FeatureSetRequest featureSetRequest = FeatureSetRequest.newBuilder() .setSpec(featureSetSpec) @@ -150,8 +176,7 @@ public void populateCache() { featureSetCache.invalidateAll(); featureSetCache.putAll(featureSetMap); - featureToFeatureSetMapping.clear(); - featureToFeatureSetMapping.putAll(getFeatureToFeatureSetMapping(featureSetMap)); + featureToFeatureSetMapping = getFeatureToFeatureSetMapping(featureSetMap); featureSetsCount.set(featureSetCache.size()); cacheLastUpdated.set(System.currentTimeMillis()); @@ -191,21 +216,81 @@ private Map getFeatureSetMap() { return featureSets; } + /** + * Generate a feature to feature set mapping from the given feature sets map. Accounts for + * variations (missing project, feature_set) in string feature references generated by creating + * multiple entries in the returned mapping for each variation. + * + * @param featureSets map of feature set name to feature set specs + * @return mapping of string feature references to name of feature sets + */ private Map getFeatureToFeatureSetMapping( Map featureSets) { - HashMap mapping = new HashMap<>(); - - for (FeatureSetSpec featureSetSpec : featureSets.values()) { - for (FeatureSpec featureSpec : featureSetSpec.getFeaturesList()) { - FeatureReference featureRef = - FeatureReference.newBuilder() - .setProject(featureSetSpec.getProject()) - .setName(featureSpec.getName()) - .build(); - mapping.put( - generateFeatureStringRef(featureRef), generateFeatureSetStringRef(featureSetSpec)); - } - } + Map mapping = new HashMap<>(); + + featureSets.values().stream() + .forEach( + featureSetSpec -> { + for (FeatureSpec featureSpec : featureSetSpec.getFeaturesList()) { + // Register the different permutations of string feature references + // that refers to this feature in the feature to featureset mapping. + + // Features in FeatureSets in default project can be referenced without project. + boolean isInDefaultProject = + featureSetSpec.getProject().equals(DEFAULT_PROJECT_NAME); + + for (boolean hasProject : new boolean[] {true, false}) { + if (!isInDefaultProject && !hasProject) continue; + // Features can be referenced without a featureset if there are no conflicts. + for (boolean hasFeatureSet : new boolean[] {true, false}) { + // Get mapping between string feature reference and featureset + Pair singleMapping = + this.generateFeatureToFeatureSetMapping( + featureSpec, featureSetSpec, hasProject, hasFeatureSet); + String featureRef = singleMapping.getKey(); + String featureSetRef = singleMapping.getValue(); + // Check if another feature set has already mapped to this + // string feature reference. if so mark the conflict. + if (mapping.containsKey(featureRef)) { + mapping.put(featureRef, FEATURE_SET_CONFLICT_FLAG); + } else { + mapping.put(featureRef, featureSetRef); + } + } + } + } + }); + return mapping; } + + /** + * Generate a single mapping between the given feature and the featureset. Maps a feature + * reference refering to the given feature to the corresponding featureset's name. + * + * @param featureSpec specifying the feature to create mapping for. + * @param featureSetSpec specifying the feature set to create mapping for. + * @param hasProject whether generated mapping's string feature ref has a project. + * @param hasFeatureSet whether generated mapping's string feature ref has a featureSet. + * @return a pair mapping a string feature reference to a featureset name. + */ + private Pair generateFeatureToFeatureSetMapping( + FeatureSpec featureSpec, + FeatureSetSpec featureSetSpec, + boolean hasProject, + boolean hasFeatureSet) { + FeatureReference.Builder featureRef = + FeatureReference.newBuilder() + .setProject(featureSetSpec.getProject()) + .setFeatureSet(featureSetSpec.getName()) + .setName(featureSpec.getName()); + if (!hasProject) { + featureRef = featureRef.clearProject(); + } + if (!hasFeatureSet) { + featureRef = featureRef.clearFeatureSet(); + } + return Pair.of( + generateFeatureStringRef(featureRef.build()), generateFeatureSetStringRef(featureSetSpec)); + } } diff --git a/serving/src/main/java/feast/serving/util/RefUtil.java b/serving/src/main/java/feast/serving/util/RefUtil.java index 018b440c503..e3c36f1f9f6 100644 --- a/serving/src/main/java/feast/serving/util/RefUtil.java +++ b/serving/src/main/java/feast/serving/util/RefUtil.java @@ -21,7 +21,13 @@ public class RefUtil { public static String generateFeatureStringRef(FeatureReference featureReference) { - String ref = String.format("%s/%s", featureReference.getProject(), featureReference.getName()); + String ref = featureReference.getName(); + if (!featureReference.getFeatureSet().isEmpty()) { + ref = featureReference.getFeatureSet() + ":" + ref; + } + if (!featureReference.getProject().isEmpty()) { + ref = featureReference.getProject() + "/" + ref; + } return ref; } diff --git a/serving/src/test/java/feast/serving/controller/ServingServiceGRpcControllerTest.java b/serving/src/test/java/feast/serving/controller/ServingServiceGRpcControllerTest.java index bf8ed710ca7..5c8308daea5 100644 --- a/serving/src/test/java/feast/serving/controller/ServingServiceGRpcControllerTest.java +++ b/serving/src/test/java/feast/serving/controller/ServingServiceGRpcControllerTest.java @@ -51,10 +51,8 @@ public void setUp() { validRequest = GetOnlineFeaturesRequest.newBuilder() - .addFeatures( - FeatureReference.newBuilder().setName("feature1").setProject("project").build()) - .addFeatures( - FeatureReference.newBuilder().setName("feature2").setProject("project").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature1").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature2").build()) .addEntityRows( EntityRow.newBuilder() .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) diff --git a/serving/src/test/java/feast/serving/service/CachedSpecServiceTest.java b/serving/src/test/java/feast/serving/service/CachedSpecServiceTest.java index 66a443ae777..85f590d5238 100644 --- a/serving/src/test/java/feast/serving/service/CachedSpecServiceTest.java +++ b/serving/src/test/java/feast/serving/service/CachedSpecServiceTest.java @@ -24,22 +24,19 @@ import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -import com.google.common.collect.Lists; import feast.proto.core.CoreServiceProto.ListFeatureSetsRequest; import feast.proto.core.CoreServiceProto.ListFeatureSetsResponse; import feast.proto.core.FeatureSetProto; import feast.proto.core.FeatureSetProto.FeatureSetSpec; import feast.proto.core.FeatureSetProto.FeatureSpec; import feast.proto.core.StoreProto.Store; -import feast.proto.core.StoreProto.Store.RedisConfig; -import feast.proto.core.StoreProto.Store.StoreType; import feast.proto.core.StoreProto.Store.Subscription; import feast.proto.serving.ServingAPIProto.FeatureReference; +import feast.serving.exception.SpecRetrievalException; import feast.serving.specs.CachedSpecService; import feast.serving.specs.CoreSpecService; import feast.storage.api.retriever.FeatureSetRequest; -import java.util.Collections; -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Before; @@ -63,70 +60,60 @@ public class CachedSpecServiceTest { public void setUp() { initMocks(this); - store = - Store.newBuilder() - .setName("SERVING") - .setType(StoreType.REDIS) - .setRedisConfig(RedisConfig.newBuilder().setHost("localhost").setPort(6379)) - .addSubscriptions( - Subscription.newBuilder().setProject("project").setName("fs1").build()) - .addSubscriptions( - Subscription.newBuilder().setProject("project").setName("fs2").build()) - .build(); - - when(coreService.registerStore(store)).thenReturn(store); + this.store = Store.newBuilder().build(); + this.featureSetSpecs = new HashMap<>(); - featureSetSpecs = new LinkedHashMap<>(); - featureSetSpecs.put( + this.setupFeatureSetAndStoreSubscription( + "project", "fs1", - FeatureSetSpec.newBuilder() - .setProject("project") - .setName("fs1") - .addFeatures(FeatureSpec.newBuilder().setName("feature")) - .build()); - featureSetSpecs.put( - "fs1", - FeatureSetSpec.newBuilder() - .setProject("project") - .setName("fs1") - .addFeatures(FeatureSpec.newBuilder().setName("feature")) - .addFeatures(FeatureSpec.newBuilder().setName("feature2")) - .build()); - featureSetSpecs.put( + List.of( + FeatureSpec.newBuilder().setName("feature").build(), + FeatureSpec.newBuilder().setName("feature2").build())); + + this.setupFeatureSetAndStoreSubscription( + "default", "fs2", + List.of( + FeatureSpec.newBuilder().setName("feature3").build(), + FeatureSpec.newBuilder().setName("feature4").build(), + FeatureSpec.newBuilder().setName("feature5").build())); + + this.setupFeatureSetAndStoreSubscription( + "default", "fs3", List.of(FeatureSpec.newBuilder().setName("feature4").build())); + + when(this.coreService.registerStore(store)).thenReturn(store); + cachedSpecService = new CachedSpecService(this.coreService, this.store); + } + + private void setupFeatureSetAndStoreSubscription( + String project, String name, List featureSpecs) { + FeatureSetSpec fsSpec = FeatureSetSpec.newBuilder() - .setProject("project") - .setName("fs2") - .addFeatures(FeatureSpec.newBuilder().setName("feature3")) - .build()); - - List fs1FeatureSets = - Lists.newArrayList( - FeatureSetProto.FeatureSet.newBuilder().setSpec(featureSetSpecs.get("fs1")).build(), - FeatureSetProto.FeatureSet.newBuilder().setSpec(featureSetSpecs.get("fs1")).build()); - List fs2FeatureSets = - Lists.newArrayList( - FeatureSetProto.FeatureSet.newBuilder().setSpec(featureSetSpecs.get("fs2")).build()); - when(coreService.listFeatureSets( - ListFeatureSetsRequest.newBuilder() - .setFilter( - ListFeatureSetsRequest.Filter.newBuilder() - .setProject("project") - .setFeatureSetName("fs1") - .build()) - .build())) - .thenReturn(ListFeatureSetsResponse.newBuilder().addAllFeatureSets(fs1FeatureSets).build()); + .setProject(project) + .setName(name) + .addAllFeatures(featureSpecs) + .build(); + this.featureSetSpecs.put(String.format("%s", name), fsSpec); + + this.store = + this.store + .toBuilder() + .addSubscriptions(Subscription.newBuilder().setProject(project).setName(name).build()) + .build(); + + // collect the different versions the featureset with the given name + FeatureSetProto.FeatureSet featureSet = + FeatureSetProto.FeatureSet.newBuilder().setSpec(fsSpec).build(); + when(coreService.listFeatureSets( ListFeatureSetsRequest.newBuilder() .setFilter( ListFeatureSetsRequest.Filter.newBuilder() - .setProject("project") - .setFeatureSetName("fs2") + .setProject(project) + .setFeatureSetName(name) .build()) .build())) - .thenReturn(ListFeatureSetsResponse.newBuilder().addAllFeatureSets(fs2FeatureSets).build()); - - cachedSpecService = new CachedSpecService(coreService, store); + .thenReturn(ListFeatureSetsResponse.newBuilder().addFeatureSets(featureSet).build()); } @Test @@ -143,64 +130,95 @@ public void shouldPopulateAndReturnStore() { @Test public void shouldPopulateAndReturnFeatureSets() { + // test that CachedSpecService can retrieve fully qualified feature references. cachedSpecService.populateCache(); - FeatureReference frv1 = - FeatureReference.newBuilder().setProject("project").setName("feature").build(); - FeatureReference frv2 = - FeatureReference.newBuilder().setProject("project").setName("feature").build(); + FeatureReference fs1fr1 = + FeatureReference.newBuilder() + .setProject("project") + .setName("feature") + .setFeatureSet("fs1") + .build(); + FeatureReference fs1fr2 = + FeatureReference.newBuilder() + .setProject("project") + .setName("feature2") + .setFeatureSet("fs1") + .build(); assertThat( - cachedSpecService.getFeatureSets(Collections.singletonList(frv1)), + cachedSpecService.getFeatureSets(List.of(fs1fr1, fs1fr2)), equalTo( - Lists.newArrayList( + List.of( FeatureSetRequest.newBuilder() - .addFeatureReference(frv1) + .addFeatureReference(fs1fr1) + .addFeatureReference(fs1fr2) .setSpec(featureSetSpecs.get("fs1")) .build()))); + } + + @Test + public void shouldPopulateAndReturnFeatureSetWithDefaultProjectIfProjectNotSupplied() { + // test that CachedSpecService will use default project when project unspecified + FeatureReference fs2fr3 = + FeatureReference.newBuilder().setName("feature3").setFeatureSet("fs2").build(); + // check that this is true for references in where feature set is unspecified + FeatureReference fs2fr5 = FeatureReference.newBuilder().setName("feature5").build(); + assertThat( - cachedSpecService.getFeatureSets(Collections.singletonList(frv2)), + cachedSpecService.getFeatureSets(List.of(fs2fr3, fs2fr5)), equalTo( - Lists.newArrayList( + List.of( FeatureSetRequest.newBuilder() - .addFeatureReference(frv2) - .setSpec(featureSetSpecs.get("fs1")) + .addFeatureReference(fs2fr3) + .addFeatureReference(fs2fr5) + .setSpec(featureSetSpecs.get("fs2")) .build()))); } @Test - public void shouldPopulateAndReturnLatestFeatureSetIfVersionsNotSupplied() { - cachedSpecService.populateCache(); - FeatureReference frv1 = + public void shouldPopulateAndReturnClosestFeatureSetIfFeatureSetNotSupplied() { + // test that CachedSpecService will try to match a featureset without a featureset name in + // reference + FeatureReference fs1fr1 = FeatureReference.newBuilder().setProject("project").setName("feature").build(); + // check that this is true for reference in which project is unspecified + FeatureReference fs2fr3 = FeatureReference.newBuilder().setName("feature3").build(); + assertThat( - cachedSpecService.getFeatureSets(Collections.singletonList(frv1)), - equalTo( - Lists.newArrayList( - FeatureSetRequest.newBuilder() - .addFeatureReference(frv1) - .setSpec(featureSetSpecs.get("fs1")) - .build()))); + cachedSpecService.getFeatureSets(List.of(fs1fr1, fs2fr3)), + containsInAnyOrder( + List.of( + FeatureSetRequest.newBuilder() + .addFeatureReference(fs1fr1) + .setSpec(featureSetSpecs.get("fs1")) + .build(), + FeatureSetRequest.newBuilder() + .addFeatureReference(fs2fr3) + .setSpec(featureSetSpecs.get("fs2")) + .build()) + .toArray())); } @Test public void shouldPopulateAndReturnFeatureSetsGivenFeaturesFromDifferentFeatureSets() { cachedSpecService.populateCache(); - FeatureReference frv1 = + FeatureReference fs1fr1 = FeatureReference.newBuilder().setProject("project").setName("feature").build(); - FeatureReference fr3 = - FeatureReference.newBuilder().setProject("project").setName("feature3").build(); + + FeatureReference fs2fr3 = + FeatureReference.newBuilder().setProject("default").setName("feature3").build(); assertThat( - cachedSpecService.getFeatureSets(Lists.newArrayList(frv1, fr3)), + cachedSpecService.getFeatureSets(List.of(fs1fr1, fs2fr3)), containsInAnyOrder( - Lists.newArrayList( + List.of( FeatureSetRequest.newBuilder() - .addFeatureReference(frv1) + .addFeatureReference(fs1fr1) .setSpec(featureSetSpecs.get("fs1")) .build(), FeatureSetRequest.newBuilder() - .addFeatureReference(fr3) + .addFeatureReference(fs2fr3) .setSpec(featureSetSpecs.get("fs2")) .build()) .toArray())); @@ -215,13 +233,26 @@ public void shouldPopulateAndReturnFeatureSetGivenFeaturesFromSameFeatureSet() { FeatureReference.newBuilder().setProject("project").setName("feature2").build(); assertThat( - cachedSpecService.getFeatureSets(Lists.newArrayList(fr1, fr2)), + cachedSpecService.getFeatureSets(List.of(fr1, fr2)), equalTo( - Lists.newArrayList( + List.of( FeatureSetRequest.newBuilder() .addFeatureReference(fr1) .addFeatureReference(fr2) .setSpec(featureSetSpecs.get("fs1")) .build()))); } + + @Test + public void shouldThrowExceptionWhenMultipleFeatureSetMapToFeatureReference() + throws SpecRetrievalException { + // both fs2 and fs3 have the feature with the same name. + // using a generic feature reference only specifying the feature's name + // should cause a multiple feature sets to match and throw an error + FeatureReference fs2fr4 = FeatureReference.newBuilder().setName("feature4").build(); + FeatureReference fs3fr4 = FeatureReference.newBuilder().setName("feature4").build(); + + expectedException.expect(SpecRetrievalException.class); + cachedSpecService.getFeatureSets(List.of(fs2fr4, fs3fr4)); + } } diff --git a/serving/src/test/java/feast/serving/service/OnlineServingServiceTest.java b/serving/src/test/java/feast/serving/service/OnlineServingServiceTest.java index 3148d0e030d..6358460a070 100644 --- a/serving/src/test/java/feast/serving/service/OnlineServingServiceTest.java +++ b/serving/src/test/java/feast/serving/service/OnlineServingServiceTest.java @@ -69,10 +69,8 @@ public void setUp() { public void shouldReturnResponseWithValuesIfKeysPresent() { GetOnlineFeaturesRequest request = GetOnlineFeaturesRequest.newBuilder() - .addFeatures( - FeatureReference.newBuilder().setName("feature1").setProject("project").build()) - .addFeatures( - FeatureReference.newBuilder().setName("feature2").setProject("project").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature1").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature2").build()) .addEntityRows( EntityRow.newBuilder() .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) @@ -95,7 +93,6 @@ public void shouldReturnResponseWithValuesIfKeysPresent() { Field.newBuilder().setName("entity2").setValue(strValue("a")).build(), Field.newBuilder().setName("feature1").setValue(intValue(1)).build(), Field.newBuilder().setName("feature2").setValue(intValue(1)).build())) - .setFeatureSet("featureSet") .build(), FeatureRow.newBuilder() .setEventTimestamp(Timestamp.newBuilder().setSeconds(100)) @@ -105,7 +102,6 @@ public void shouldReturnResponseWithValuesIfKeysPresent() { Field.newBuilder().setName("entity2").setValue(strValue("b")).build(), Field.newBuilder().setName("feature1").setValue(intValue(2)).build(), Field.newBuilder().setName("feature2").setValue(intValue(2)).build())) - .setFeatureSet("featureSet") .build()); FeatureSetRequest featureSetRequest = @@ -127,90 +123,14 @@ public void shouldReturnResponseWithValuesIfKeysPresent() { FieldValues.newBuilder() .putFields("entity1", intValue(1)) .putFields("entity2", strValue("a")) - .putFields("project/feature1", intValue(1)) - .putFields("project/feature2", intValue(1))) + .putFields("feature1", intValue(1)) + .putFields("feature2", intValue(1))) .addFieldValues( FieldValues.newBuilder() .putFields("entity1", intValue(2)) .putFields("entity2", strValue("b")) - .putFields("project/feature1", intValue(2)) - .putFields("project/feature2", intValue(2))) - .build(); - GetOnlineFeaturesResponse actual = onlineServingService.getOnlineFeatures(request); - assertThat( - responseToMapList(actual), containsInAnyOrder(responseToMapList(expected).toArray())); - } - - @Test - public void shouldReturnKeysWithoutVersionIfNotProvided() { - GetOnlineFeaturesRequest request = - GetOnlineFeaturesRequest.newBuilder() - .addFeatures( - FeatureReference.newBuilder().setName("feature1").setProject("project").build()) - .addFeatures( - FeatureReference.newBuilder().setName("feature2").setProject("project").build()) - .addEntityRows( - EntityRow.newBuilder() - .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) - .putFields("entity1", intValue(1)) - .putFields("entity2", strValue("a"))) - .addEntityRows( - EntityRow.newBuilder() - .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) - .putFields("entity1", intValue(2)) - .putFields("entity2", strValue("b"))) - .build(); - - List featureRows = - Lists.newArrayList( - FeatureRow.newBuilder() - .setEventTimestamp(Timestamp.newBuilder().setSeconds(100)) - .addAllFields( - Lists.newArrayList( - Field.newBuilder().setName("entity1").setValue(intValue(1)).build(), - Field.newBuilder().setName("entity2").setValue(strValue("a")).build(), - Field.newBuilder().setName("feature1").setValue(intValue(1)).build(), - Field.newBuilder().setName("feature2").setValue(intValue(1)).build())) - .setFeatureSet("featureSet") - .build(), - FeatureRow.newBuilder() - .setEventTimestamp(Timestamp.newBuilder().setSeconds(100)) - .addAllFields( - Lists.newArrayList( - Field.newBuilder().setName("entity1").setValue(intValue(2)).build(), - Field.newBuilder().setName("entity2").setValue(strValue("b")).build(), - Field.newBuilder().setName("feature1").setValue(intValue(2)).build(), - Field.newBuilder().setName("feature2").setValue(intValue(2)).build())) - .setFeatureSet("featureSet") - .build()); - - FeatureSetRequest featureSetRequest = - FeatureSetRequest.newBuilder() - .addAllFeatureReferences(request.getFeaturesList()) - .setSpec(getFeatureSetSpec()) - .build(); - - when(specService.getFeatureSets(request.getFeaturesList())) - .thenReturn(Collections.singletonList(featureSetRequest)); - when(retriever.getOnlineFeatures( - request.getEntityRowsList(), Collections.singletonList(featureSetRequest))) - .thenReturn(Collections.singletonList(featureRows)); - when(tracer.buildSpan(ArgumentMatchers.any())).thenReturn(Mockito.mock(SpanBuilder.class)); - - GetOnlineFeaturesResponse expected = - GetOnlineFeaturesResponse.newBuilder() - .addFieldValues( - FieldValues.newBuilder() - .putFields("entity1", intValue(1)) - .putFields("entity2", strValue("a")) - .putFields("project/feature1", intValue(1)) - .putFields("project/feature2", intValue(1))) - .addFieldValues( - FieldValues.newBuilder() - .putFields("entity1", intValue(2)) - .putFields("entity2", strValue("b")) - .putFields("project/feature1", intValue(2)) - .putFields("project/feature2", intValue(2))) + .putFields("feature1", intValue(2)) + .putFields("feature2", intValue(2))) .build(); GetOnlineFeaturesResponse actual = onlineServingService.getOnlineFeatures(request); assertThat( @@ -222,10 +142,8 @@ public void shouldReturnResponseWithUnsetValuesIfKeysNotPresent() { // some keys not present, should have empty values GetOnlineFeaturesRequest request = GetOnlineFeaturesRequest.newBuilder() - .addFeatures( - FeatureReference.newBuilder().setName("feature1").setProject("project").build()) - .addFeatures( - FeatureReference.newBuilder().setName("feature2").setProject("project").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature1").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature2").build()) .addEntityRows( EntityRow.newBuilder() .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) @@ -275,14 +193,14 @@ public void shouldReturnResponseWithUnsetValuesIfKeysNotPresent() { FieldValues.newBuilder() .putFields("entity1", intValue(1)) .putFields("entity2", strValue("a")) - .putFields("project/feature1", intValue(1)) - .putFields("project/feature2", intValue(1))) + .putFields("feature1", intValue(1)) + .putFields("feature2", intValue(1))) .addFieldValues( FieldValues.newBuilder() .putFields("entity1", intValue(2)) .putFields("entity2", strValue("b")) - .putFields("project/feature1", Value.newBuilder().build()) - .putFields("project/feature2", Value.newBuilder().build())) + .putFields("feature1", Value.newBuilder().build()) + .putFields("feature2", Value.newBuilder().build())) .build(); GetOnlineFeaturesResponse actual = onlineServingService.getOnlineFeatures(request); assertThat( @@ -294,10 +212,8 @@ public void shouldReturnResponseWithUnsetValuesIfMaxAgeIsExceeded() { // keys present, but too stale comp. to maxAge GetOnlineFeaturesRequest request = GetOnlineFeaturesRequest.newBuilder() - .addFeatures( - FeatureReference.newBuilder().setName("feature1").setProject("project").build()) - .addFeatures( - FeatureReference.newBuilder().setName("feature2").setProject("project").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature1").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature2").build()) .addEntityRows( EntityRow.newBuilder() .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) @@ -320,7 +236,7 @@ public void shouldReturnResponseWithUnsetValuesIfMaxAgeIsExceeded() { Field.newBuilder().setName("entity2").setValue(strValue("a")).build(), Field.newBuilder().setName("feature1").setValue(intValue(1)).build(), Field.newBuilder().setName("feature2").setValue(intValue(1)).build())) - .setFeatureSet("featureSet") + .setFeatureSet("project/featureSet") .build(), FeatureRow.newBuilder() .setEventTimestamp( @@ -331,7 +247,7 @@ public void shouldReturnResponseWithUnsetValuesIfMaxAgeIsExceeded() { Field.newBuilder().setName("entity2").setValue(strValue("b")).build(), Field.newBuilder().setName("feature1").setValue(intValue(2)).build(), Field.newBuilder().setName("feature2").setValue(intValue(2)).build())) - .setFeatureSet("featureSet") + .setFeatureSet("project/featureSet") .build()); FeatureSetSpec spec = @@ -355,14 +271,14 @@ public void shouldReturnResponseWithUnsetValuesIfMaxAgeIsExceeded() { FieldValues.newBuilder() .putFields("entity1", intValue(1)) .putFields("entity2", strValue("a")) - .putFields("project/feature1", intValue(1)) - .putFields("project/feature2", intValue(1))) + .putFields("feature1", intValue(1)) + .putFields("feature2", intValue(1))) .addFieldValues( FieldValues.newBuilder() .putFields("entity1", intValue(2)) .putFields("entity2", strValue("b")) - .putFields("project/feature1", Value.newBuilder().build()) - .putFields("project/feature2", Value.newBuilder().build())) + .putFields("feature1", Value.newBuilder().build()) + .putFields("feature2", Value.newBuilder().build())) .build(); GetOnlineFeaturesResponse actual = onlineServingService.getOnlineFeatures(request); assertThat( @@ -374,8 +290,7 @@ public void shouldFilterOutUndesiredRows() { // requested rows less than the rows available in the featureset GetOnlineFeaturesRequest request = GetOnlineFeaturesRequest.newBuilder() - .addFeatures( - FeatureReference.newBuilder().setName("feature1").setProject("project").build()) + .addFeatures(FeatureReference.newBuilder().setName("feature1").build()) .addEntityRows( EntityRow.newBuilder() .setEntityTimestamp(Timestamp.newBuilder().setSeconds(100)) @@ -398,7 +313,6 @@ public void shouldFilterOutUndesiredRows() { Field.newBuilder().setName("entity2").setValue(strValue("a")).build(), Field.newBuilder().setName("feature1").setValue(intValue(1)).build(), Field.newBuilder().setName("feature2").setValue(intValue(1)).build())) - .setFeatureSet("featureSet") .build(), FeatureRow.newBuilder() .setEventTimestamp(Timestamp.newBuilder().setSeconds(100)) @@ -408,7 +322,6 @@ public void shouldFilterOutUndesiredRows() { Field.newBuilder().setName("entity2").setValue(strValue("b")).build(), Field.newBuilder().setName("feature1").setValue(intValue(2)).build(), Field.newBuilder().setName("feature2").setValue(intValue(2)).build())) - .setFeatureSet("featureSet") .build()); FeatureSetRequest featureSetRequest = @@ -430,12 +343,12 @@ public void shouldFilterOutUndesiredRows() { FieldValues.newBuilder() .putFields("entity1", intValue(1)) .putFields("entity2", strValue("a")) - .putFields("project/feature1", intValue(1))) + .putFields("feature1", intValue(1))) .addFieldValues( FieldValues.newBuilder() .putFields("entity1", intValue(2)) .putFields("entity2", strValue("b")) - .putFields("project/feature1", intValue(2))) + .putFields("feature1", intValue(2))) .build(); GetOnlineFeaturesResponse actual = onlineServingService.getOnlineFeatures(request); assertThat( @@ -458,7 +371,6 @@ private Value strValue(String val) { private FeatureSetSpec getFeatureSetSpec() { return FeatureSetSpec.newBuilder() - .setProject("project") .setName("featureSet") .addEntities(EntitySpec.newBuilder().setName("entity1")) .addEntities(EntitySpec.newBuilder().setName("entity2")) diff --git a/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/FeatureSetQueryInfo.java b/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/FeatureSetQueryInfo.java index a99411f87b9..befdc564904 100644 --- a/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/FeatureSetQueryInfo.java +++ b/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/FeatureSetQueryInfo.java @@ -16,6 +16,7 @@ */ package feast.storage.connectors.bigquery.retriever; +import feast.proto.serving.ServingAPIProto.FeatureReference; import java.util.List; public class FeatureSetQueryInfo { @@ -24,7 +25,7 @@ public class FeatureSetQueryInfo { private final String name; private final long maxAge; private final List entities; - private final List features; + private final List features; private final String table; public FeatureSetQueryInfo( @@ -32,7 +33,7 @@ public FeatureSetQueryInfo( String name, long maxAge, List entities, - List features, + List features, String table) { this.project = project; this.name = name; @@ -68,7 +69,7 @@ public List getEntities() { return entities; } - public List getFeatures() { + public List getFeatures() { return features; } diff --git a/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/QueryTemplater.java b/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/QueryTemplater.java index 84899d69adb..969efb36c38 100644 --- a/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/QueryTemplater.java +++ b/storage/connectors/bigquery/src/main/java/feast/storage/connectors/bigquery/retriever/QueryTemplater.java @@ -79,10 +79,7 @@ public static List getFeatureSetInfos( Duration maxAge = spec.getMaxAge(); List fsEntities = spec.getEntitiesList().stream().map(EntitySpec::getName).collect(Collectors.toList()); - List features = - featureSetRequest.getFeatureReferences().stream() - .map(FeatureReference::getName) - .collect(Collectors.toList()); + List features = featureSetRequest.getFeatureReferences().asList(); featureSetInfos.add( new FeatureSetQueryInfo( spec.getProject(), spec.getName(), maxAge.getSeconds(), fsEntities, features, "")); diff --git a/storage/connectors/bigquery/src/main/resources/templates/join_featuresets.sql b/storage/connectors/bigquery/src/main/resources/templates/join_featuresets.sql index 10aafb05092..ddddac8d2cf 100644 --- a/storage/connectors/bigquery/src/main/resources/templates/join_featuresets.sql +++ b/storage/connectors/bigquery/src/main/resources/templates/join_featuresets.sql @@ -7,8 +7,8 @@ SELECT * FROM `{{ leftTableName }}` LEFT JOIN ( SELECT uuid, - {% for featureName in featureSet.features %} - {{ featureSet.project }}_{{ featureName }}{% if loop.last %}{% else %}, {% endif %} + {% for feature in featureSet.features %} + {{ featureSet.project }}__{{ featureSet.name }}__{{ feature.name }}{% if loop.last %}{% else %}, {% endif %} {% endfor %} FROM `{{ featureSet.table }}` ) USING (uuid) @@ -17,8 +17,8 @@ LEFT JOIN ( event_timestamp, {{ entities | join(', ') }} {% for featureSet in featureSets %} - {% for featureName in featureSet.features %} - ,{{ featureSet.project }}_{{ featureName }} as {{ featureName }} + {% for feature in featureSet.features %} + ,{{ featureSet.project }}__{{ featureSet.name }}__{{ feature.name }} as {% if feature.featureSet != "" %}{{ featureSet.name }}__{% endif %}{{ feature.name }} {% endfor %} {% endfor %} -FROM joined \ No newline at end of file +FROM joined diff --git a/storage/connectors/bigquery/src/main/resources/templates/single_featureset_pit_join.sql b/storage/connectors/bigquery/src/main/resources/templates/single_featureset_pit_join.sql index c02bf6b46c1..24bdab2c29c 100644 --- a/storage/connectors/bigquery/src/main/resources/templates/single_featureset_pit_join.sql +++ b/storage/connectors/bigquery/src/main/resources/templates/single_featureset_pit_join.sql @@ -47,8 +47,8 @@ SELECT uuid, event_timestamp, {{ featureSet.entities | join(', ')}}, - {% for featureName in featureSet.features %} - IF(event_timestamp >= {{ featureSet.project }}_{{ featureSet.name }}_feature_timestamp {% if featureSet.maxAge == 0 %}{% else %}AND Timestamp_sub(event_timestamp, interval {{ featureSet.maxAge }} second) < {{ featureSet.project }}_{{ featureSet.name }}_feature_timestamp{% endif %}, {{ featureSet.project }}_{{ featureName }}, NULL) as {{ featureSet.project }}_{{ featureName }}{% if loop.last %}{% else %}, {% endif %} + {% for feature in featureSet.features %} + IF(event_timestamp >= {{ featureSet.project }}_{{ featureSet.name }}_feature_timestamp {% if featureSet.maxAge == 0 %}{% else %}AND Timestamp_sub(event_timestamp, interval {{ featureSet.maxAge }} second) < {{ featureSet.project }}_{{ featureSet.name }}_feature_timestamp{% endif %}, {{ featureSet.project }}__{{ featureSet.name }}__{{ feature.name }}, NULL) as {{ featureSet.project }}__{{ featureSet.name }}__{{ feature.name }}{% if loop.last %}{% else %}, {% endif %} {% endfor %} FROM ( SELECT @@ -70,8 +70,8 @@ SELECT event_timestamp as {{ featureSet.project }}_{{ featureSet.name }}_feature_timestamp, created_timestamp, {{ featureSet.entities | join(', ')}}, - {% for featureName in featureSet.features %} - {{ featureName }} as {{ featureSet.project }}_{{ featureName }}{% if loop.last %}{% else %}, {% endif %} + {% for feature in featureSet.features %} + {{ feature.name }} as {{ featureSet.project }}__{{ featureSet.name }}__{{ feature.name }}{% if loop.last %}{% else %}, {% endif %} {% endfor %} FROM `{{ projectId }}.{{ datasetId }}.{{ featureSet.project }}_{{ featureSet.name }}` WHERE event_timestamp <= '{{maxTimestamp}}' {% if featureSet.maxAge == 0 %}{% else %}AND event_timestamp >= Timestamp_sub(TIMESTAMP '{{ minTimestamp }}', interval {{ featureSet.maxAge }} second){% endif %} diff --git a/tests/e2e/basic-ingest-redis-serving.py b/tests/e2e/basic-ingest-redis-serving.py index adf0669a715..311f1282477 100644 --- a/tests/e2e/basic-ingest-redis-serving.py +++ b/tests/e2e/basic-ingest-redis-serving.py @@ -15,6 +15,7 @@ from feast.client import Client from feast.feature_set import FeatureSet, FeatureSetRef from feast.type_map import ValueType +from feast.constants import FEAST_DEFAULT_OPTIONS, CONFIG_PROJECT_KEY from google.protobuf.duration_pb2 import Duration from datetime import datetime import pytz @@ -29,7 +30,6 @@ FLOAT_TOLERANCE = 0.00001 PROJECT_NAME = 'basic_' + uuid.uuid4().hex.upper()[0:6] - @pytest.fixture(scope='module') def core_url(pytestconfig): return pytestconfig.getoption("core_url") @@ -51,7 +51,6 @@ def client(core_url, serving_url, allow_dirty): # Get client for core and serving client = Client(core_url=core_url, serving_url=serving_url) client.create_project(PROJECT_NAME) - client.set_project(PROJECT_NAME) # Ensure Feast core is active, but empty if not allow_dirty: @@ -63,80 +62,94 @@ def client(core_url, serving_url, allow_dirty): return client - -@pytest.fixture(scope='module') -def basic_dataframe(): +def basic_dataframe(entities, features, ingest_time, n_size): offset = random.randint(1000, 100000) # ensure a unique key space is used - return pd.DataFrame( - { - "datetime": [datetime.utcnow().replace(tzinfo=pytz.utc) for _ in - range(5)], - "customer_id": [offset + inc for inc in range(5)], - "daily_transactions": [np.random.rand() for _ in range(5)], - "total_transactions": [512 for _ in range(5)], - } - ) - + df_dict = { + "datetime": [ingest_time.replace(tzinfo=pytz.utc) for _ in + range(n_size)], + } + for entity_name in entities: + df_dict[entity_name] = list(range(1, n_size + 1)) + for feature_name in features: + df_dict[feature_name] = [np.random.rand() for _ in range(n_size)] + return pd.DataFrame(df_dict) + + +@pytest.fixture(scope="module") +def ingest_time(): + return datetime.utcnow() + +@pytest.fixture(scope="module") +def cust_trans_df(ingest_time): + return basic_dataframe(entities=["customer_id"], + features=["daily_transactions", "total_transactions"], + ingest_time=ingest_time, + n_size=5) + +@pytest.fixture(scope="module") +def driver_df(ingest_time): + return basic_dataframe(entities=["driver_id"], + features=["rating", "cost"], + ingest_time=ingest_time, + n_size=5) @pytest.mark.timeout(45) @pytest.mark.run(order=10) def test_basic_register_feature_set_success(client): - # Load feature set from file + # Register feature set without project cust_trans_fs_expected = FeatureSet.from_yaml("basic/cust_trans_fs.yaml") + driver_fs_expected = FeatureSet.from_yaml("basic/driver_fs.yaml") + client.apply(cust_trans_fs_expected) + client.apply(driver_fs_expected) + cust_trans_fs_actual = client.get_feature_set("customer_transactions") + assert cust_trans_fs_actual == cust_trans_fs_expected + driver_fs_actual = client.get_feature_set("driver") + assert driver_fs_actual == driver_fs_expected + # Register feature set with project + cust_trans_fs_expected = FeatureSet.from_yaml("basic/cust_trans_fs.yaml") client.set_project(PROJECT_NAME) - - # Register feature set client.apply(cust_trans_fs_expected) - - cust_trans_fs_actual = client.get_feature_set(name="customer_transactions") - + cust_trans_fs_actual = client.get_feature_set("customer_transactions", + project=PROJECT_NAME) assert cust_trans_fs_actual == cust_trans_fs_expected - if cust_trans_fs_actual is None: - raise Exception( - "Client cannot retrieve 'customer_transactions' FeatureSet " - "after registration. Either Feast Core does not save the " - "FeatureSet correctly or the client needs to wait longer for FeatureSet " - "to be committed." - ) - + # reset client's project for other tests + client.set_project() @pytest.mark.timeout(300) @pytest.mark.run(order=11) -def test_basic_ingest_success(client, basic_dataframe): - client.set_project(PROJECT_NAME) - +def test_basic_ingest_success(client, cust_trans_df, driver_df): cust_trans_fs = client.get_feature_set(name="customer_transactions") + driver_fs = client.get_feature_set(name="driver") # Ingest customer transaction data - client.ingest(cust_trans_fs, basic_dataframe) + client.ingest(cust_trans_fs, cust_trans_df) + client.ingest(driver_fs, driver_df) time.sleep(5) @pytest.mark.timeout(45) @pytest.mark.run(order=12) -def test_basic_retrieve_online_success(client, basic_dataframe): +def test_basic_retrieve_online_success(client, cust_trans_df): # Poll serving for feature values until the correct values are returned while True: time.sleep(1) - - client.set_project(PROJECT_NAME) - response = client.get_online_features( entity_rows=[ GetOnlineFeaturesRequest.EntityRow( fields={ "customer_id": Value( - int64_val=basic_dataframe.iloc[0]["customer_id"] + int64_val=cust_trans_df.iloc[0]["customer_id"] ) } ) ], + # Test retrieve with different variations of the string feature refs feature_refs=[ "daily_transactions", "total_transactions", - ], + ] ) # type: GetOnlineFeaturesResponse if response is None: @@ -144,23 +157,78 @@ def test_basic_retrieve_online_success(client, basic_dataframe): returned_daily_transactions = float( response.field_values[0] - .fields[PROJECT_NAME + "/daily_transactions"] - .float_val + .fields["daily_transactions"] + .float_val ) sent_daily_transactions = float( - basic_dataframe.iloc[0]["daily_transactions"]) + cust_trans_df.iloc[0]["daily_transactions"]) if math.isclose( - sent_daily_transactions, - returned_daily_transactions, - abs_tol=FLOAT_TOLERANCE, + sent_daily_transactions, + returned_daily_transactions, + abs_tol=FLOAT_TOLERANCE, ): break +@pytest.mark.timeout(45) +@pytest.mark.run(order=13) +def test_basic_retrieve_online_multiple_featureset(client, cust_trans_df, driver_df): + # Poll serving for feature values until the correct values are returned + while True: + time.sleep(1) + # Test retrieve with different variations of the string feature refs + # ie feature set inference for feature refs without specified feature set + feature_ref_df_mapping = [ + ("customer_transactions:daily_transactions", cust_trans_df), + ("driver:rating", driver_df), + ("total_transactions", cust_trans_df), + ] + response = client.get_online_features( + entity_rows=[ + GetOnlineFeaturesRequest.EntityRow( + fields={ + "customer_id": Value( + int64_val=cust_trans_df.iloc[0]["customer_id"] + ), + "driver_id": Value( + int64_val=driver_df.iloc[0]["driver_id"] + ) + } + ) + ], + feature_refs=[mapping[0] for mapping in feature_ref_df_mapping], + ) # type: GetOnlineFeaturesResponse + + if response is None: + continue + + def check_response(ingest_df, response, feature_ref): + returned_value = float( + response.field_values[0] + .fields[feature_ref] + .float_val + ) + feature_ref_splits = feature_ref.split(":") + if len(feature_ref_splits) == 1: + feature_name = feature_ref + else: + _, feature_name = feature_ref_splits + + sent_value = float( + ingest_df.iloc[0][feature_name]) + + return math.isclose( + sent_value, + returned_value, + abs_tol=FLOAT_TOLERANCE, + ) + if all([check_response(df, response, ref) for ref, df in feature_ref_df_mapping]): + break + @pytest.mark.timeout(300) @pytest.mark.run(order=19) -def test_basic_ingest_jobs(client, basic_dataframe): +def test_basic_ingest_jobs(client): # list ingestion jobs given featureset cust_trans_fs = client.get_feature_set(name="customer_transactions") ingest_jobs = client.list_ingest_jobs( @@ -327,7 +395,7 @@ def test_all_types_retrieve_online_success(client, all_types_dataframe): returned_float_list = ( response.field_values[0] - .fields[PROJECT_NAME + "/float_list_feature"] + .fields["float_list_feature"] .float_list_val.val ) @@ -445,7 +513,7 @@ def test_large_volume_retrieve_online_success(client, large_volume_dataframe): returned_daily_transactions = float( response.field_values[0] - .fields[PROJECT_NAME + "/daily_transactions_large"] + .fields["daily_transactions_large"] .float_val ) sent_daily_transactions = float( diff --git a/tests/e2e/basic/driver_fs.yaml b/tests/e2e/basic/driver_fs.yaml new file mode 100644 index 00000000000..f25ca956782 --- /dev/null +++ b/tests/e2e/basic/driver_fs.yaml @@ -0,0 +1,12 @@ +kind: feature_set +spec: + name: driver + entities: + - name: driver_id + valueType: INT64 + features: + - name: rating + valueType: FLOAT + - name: cost + valueType: FLOAT + maxAge: 3600s diff --git a/tests/e2e/bq-batch-retrieval.py b/tests/e2e/bq-batch-retrieval.py index a4d8a729ef7..99b88a8dff7 100644 --- a/tests/e2e/bq-batch-retrieval.py +++ b/tests/e2e/bq-batch-retrieval.py @@ -27,7 +27,6 @@ PROJECT_NAME = "batch_" + uuid.uuid4().hex.upper()[0:6] - @pytest.fixture(scope="module") def core_url(pytestconfig): return pytestconfig.getoption("core_url") @@ -53,7 +52,6 @@ def client(core_url, serving_url, allow_dirty): # Get client for core and serving client = Client(core_url=core_url, serving_url=serving_url) client.create_project(PROJECT_NAME) - client.set_project(PROJECT_NAME) # Ensure Feast core is active, but empty if not allow_dirty: @@ -167,7 +165,8 @@ def test_batch_get_batch_features_with_file(client): time.sleep(15) feature_retrieval_job = client.get_batch_features( entity_rows="file://file_feature_set.avro", - feature_refs=[f"{PROJECT_NAME}/feature_value1"], + feature_refs=["feature_value1"], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe() @@ -220,7 +219,9 @@ def test_batch_get_batch_features_with_gs_path(client, gcs_path): time.sleep(15) feature_retrieval_job = client.get_batch_features( - entity_rows=f"{gcs_path}{ts}/*", feature_refs=[f"{PROJECT_NAME}/feature_value2"] + entity_rows=f"{gcs_path}{ts}/*", + feature_refs=["feature_value2"], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe() @@ -259,7 +260,8 @@ def test_batch_order_by_creation_time(client): client.ingest(proc_time_fs, correct_df) feature_retrieval_job = client.get_batch_features( entity_rows=incorrect_df[["datetime", "entity_id"]], - feature_refs=[f"{PROJECT_NAME}/feature_value3"], + feature_refs=["feature_value3"], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe() clean_up_remote_files(feature_retrieval_job.get_avro_files()) @@ -295,7 +297,9 @@ def test_batch_additional_columns_in_entity_table(client): time.sleep(15) feature_retrieval_job = client.get_batch_features( - entity_rows=entity_df, feature_refs=[f"{PROJECT_NAME}/feature_value4"] + entity_rows=entity_df, + feature_refs=["feature_value4"], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe().sort_values(by=["entity_id"]) clean_up_remote_files(feature_retrieval_job.get_avro_files()) @@ -341,7 +345,9 @@ def test_batch_point_in_time_correctness_join(client): time.sleep(15) feature_retrieval_job = client.get_batch_features( - entity_rows=entity_df, feature_refs=[f"{PROJECT_NAME}/feature_value5"] + entity_rows=entity_df, + feature_refs=["feature_value5"], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe() clean_up_remote_files(feature_retrieval_job.get_avro_files()) @@ -385,12 +391,15 @@ def test_batch_multiple_featureset_joins(client): ) time.sleep(15) + # Test retrieve with different variations of the string feature refs + # ie feature set inference for feature refs without specified feature set feature_retrieval_job = client.get_batch_features( entity_rows=entity_df, feature_refs=[ - f"{PROJECT_NAME}/feature_value6", - f"{PROJECT_NAME}/other_feature_value7", + "feature_value6", + "feature_set_2:other_feature_value7", ], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe() clean_up_remote_files(feature_retrieval_job.get_avro_files()) @@ -400,7 +409,7 @@ def test_batch_multiple_featureset_joins(client): int(i) for i in output["feature_value6"].to_list() ] assert ( - output["other_entity_id"].to_list() == output["other_feature_value7"].to_list() + output["other_entity_id"].to_list() == output["feature_set_2__other_feature_value7"].to_list() ) @@ -423,7 +432,8 @@ def test_batch_no_max_age(client): time.sleep(15) feature_retrieval_job = client.get_batch_features( entity_rows=features_8_df[["datetime", "entity_id"]], - feature_refs=[f"{PROJECT_NAME}/feature_value8"], + feature_refs=["feature_value8"], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe() @@ -504,9 +514,10 @@ def test_update_featureset_apply_featureset_and_ingest_first_subset( feature_retrieval_job = client.get_batch_features( entity_rows=update_featureset_dataframe[["datetime", "entity_id"]].iloc[:5], feature_refs=[ - f"{PROJECT_NAME}/update_feature1", - f"{PROJECT_NAME}/update_feature2", + "update_feature1", + "update_feature2", ], + project=PROJECT_NAME ) output = feature_retrieval_job.to_dataframe().sort_values(by=["entity_id"]) @@ -557,10 +568,11 @@ def test_update_featureset_update_featureset_and_ingest_second_subset( feature_retrieval_job = client.get_batch_features( entity_rows=update_featureset_dataframe[["datetime", "entity_id"]].iloc[5:], feature_refs=[ - f"{PROJECT_NAME}/update_feature1", - f"{PROJECT_NAME}/update_feature3", - f"{PROJECT_NAME}/update_feature4", + "update_feature1", + "update_feature3", + "update_feature4", ], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe().sort_values(by=["entity_id"]) @@ -579,11 +591,12 @@ def test_update_featureset_retrieve_all_fields(client, update_featureset_datafra feature_retrieval_job = client.get_batch_features( entity_rows=update_featureset_dataframe[["datetime", "entity_id"]], feature_refs=[ - f"{PROJECT_NAME}/update_feature1", - f"{PROJECT_NAME}/update_feature2", - f"{PROJECT_NAME}/update_feature3", - f"{PROJECT_NAME}/update_feature4", + "update_feature1", + "update_feature2", + "update_feature3", + "update_feature4", ], + project=PROJECT_NAME, ) feature_retrieval_job.result() @@ -594,10 +607,11 @@ def test_update_featureset_retrieve_valid_fields(client, update_featureset_dataf feature_retrieval_job = client.get_batch_features( entity_rows=update_featureset_dataframe[["datetime", "entity_id"]], feature_refs=[ - f"{PROJECT_NAME}/update_feature1", - f"{PROJECT_NAME}/update_feature3", - f"{PROJECT_NAME}/update_feature4", + "update_feature1", + "update_feature3", + "update_feature4", ], + project=PROJECT_NAME, ) output = feature_retrieval_job.to_dataframe().sort_values(by=["entity_id"]) clean_up_remote_files(feature_retrieval_job.get_avro_files())