Skip to content

Commit 3697c78

Browse files
committed
Fix Optional#get() and string comparison bugs in JobService
Clears a FIXME left from #650, get() uses that will always fail. Also fixes a bunch of == comparisons on strings, reference equality is not what you want there.
1 parent d3acb83 commit 3697c78

File tree

2 files changed

+51
-52
lines changed

2 files changed

+51
-52
lines changed

core/src/main/java/feast/core/service/JobService.java

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public JobService(
7171
}
7272
}
7373

74-
/* Job Service API */
74+
// region Job Service API
75+
7576
/**
7677
* List Ingestion Jobs in Feast matching the given request. See CoreService protobuf documentation
7778
* for more detailed documentation.
@@ -88,24 +89,24 @@ public ListIngestionJobsResponse listJobs(ListIngestionJobsRequest request)
8889

8990
// check that filter specified and not empty
9091
if (request.hasFilter()
91-
&& !(request.getFilter().getId() == ""
92-
&& request.getFilter().getStoreName() == ""
93-
&& request.getFilter().hasFeatureSetReference() == false)) {
92+
&& !(request.getFilter().getId().isEmpty()
93+
&& request.getFilter().getStoreName().isEmpty()
94+
&& !request.getFilter().hasFeatureSetReference())) {
9495
// filter jobs based on request filter
9596
ListIngestionJobsRequest.Filter filter = request.getFilter();
9697

9798
// for proto3, default value for missing values:
9899
// - numeric values (ie int) is zero
99100
// - strings is empty string
100-
if (filter.getId() != "") {
101+
if (!filter.getId().isEmpty()) {
101102
// get by id: no more filters required: found job
102103
Optional<Job> job = this.jobRepository.findById(filter.getId());
103104
if (job.isPresent()) {
104105
matchingJobIds.add(filter.getId());
105106
}
106107
} else {
107108
// multiple filters can apply together in an 'and' operation
108-
if (filter.getStoreName() != "") {
109+
if (!filter.getStoreName().isEmpty()) {
109110
// find jobs by name
110111
List<Job> jobs = this.jobRepository.findByStoreName(filter.getStoreName());
111112
Set<String> jobIds = jobs.stream().map(Job::getId).collect(Collectors.toSet());
@@ -140,7 +141,7 @@ public ListIngestionJobsResponse listJobs(ListIngestionJobsRequest request)
140141
// convert matching job models to ingestion job protos
141142
List<IngestionJobProto.IngestionJob> ingestJobs = new ArrayList<>();
142143
for (String jobId : matchingJobIds) {
143-
Job job = this.jobRepository.findById(jobId).get();
144+
Job job = this.jobRepository.findById(jobId).orElseThrow();
144145
// job that failed on start won't be converted toProto successfully
145146
// and they're irrelevant here
146147
if (job.getStatus() == JobStatus.ERROR) {
@@ -160,21 +161,20 @@ public ListIngestionJobsResponse listJobs(ListIngestionJobsRequest request)
160161
* @param request restart ingestion job request specifying which job to stop
161162
* @throws NoSuchElementException when restart job request requests to restart a nonexistent job.
162163
* @throws UnsupportedOperationException when job to be restarted is in an unsupported status
163-
* @throws InvalidProtocolBufferException on error when constructing response protobuf
164164
*/
165165
@Transactional
166-
public RestartIngestionJobResponse restartJob(RestartIngestionJobRequest request)
167-
throws InvalidProtocolBufferException {
168-
// check job exists
169-
Optional<Job> getJob = this.jobRepository.findById(request.getId());
170-
if (getJob.isEmpty()) {
171-
// FIXME: if getJob.isEmpty then constructing this error message will always throw an error...
172-
throw new NoSuchElementException(
173-
"Attempted to stop nonexistent job with id: " + getJob.get().getId());
174-
}
166+
public RestartIngestionJobResponse restartJob(RestartIngestionJobRequest request) {
167+
String jobId = request.getId();
168+
169+
Job job =
170+
this.jobRepository
171+
.findById(jobId)
172+
.orElseThrow(
173+
() ->
174+
new NoSuchElementException(
175+
"Attempted to restart nonexistent job with id: " + jobId));
175176

176177
// check job status is valid for restarting
177-
Job job = getJob.get();
178178
JobStatus status = job.getStatus();
179179
if (status.isTransitional() || status.isTerminal() || status == JobStatus.UNKNOWN) {
180180
throw new UnsupportedOperationException(
@@ -202,20 +202,20 @@ public RestartIngestionJobResponse restartJob(RestartIngestionJobRequest request
202202
* @param request stop ingestion job request specifying which job to stop
203203
* @throws NoSuchElementException when stop job request requests to stop a nonexistent job.
204204
* @throws UnsupportedOperationException when job to be stopped is in an unsupported status
205-
* @throws InvalidProtocolBufferException on error when constructing response protobuf
206205
*/
207206
@Transactional
208-
public StopIngestionJobResponse stopJob(StopIngestionJobRequest request)
209-
throws InvalidProtocolBufferException {
210-
// check job exists
211-
Optional<Job> getJob = this.jobRepository.findById(request.getId());
212-
if (getJob.isEmpty()) {
213-
throw new NoSuchElementException(
214-
"Attempted to stop nonexistent job with id: " + getJob.get().getId());
215-
}
207+
public StopIngestionJobResponse stopJob(StopIngestionJobRequest request) {
208+
String jobId = request.getId();
209+
210+
Job job =
211+
this.jobRepository
212+
.findById(jobId)
213+
.orElseThrow(
214+
() ->
215+
new NoSuchElementException(
216+
"Attempted to stop nonexistent job with id: " + jobId));
216217

217218
// check job status is valid for stopping
218-
Job job = getJob.get();
219219
JobStatus status = job.getStatus();
220220
if (status.isTerminal()) {
221221
// do nothing - job is already stopped
@@ -240,7 +240,9 @@ public StopIngestionJobResponse stopJob(StopIngestionJobRequest request)
240240
return StopIngestionJobResponse.newBuilder().build();
241241
}
242242

243-
/* Private Utility Methods */
243+
// endregion
244+
// region Private Utility Methods
245+
244246
private <T> Set<T> mergeResults(Set<T> results, Collection<T> newResults) {
245247
if (results.size() <= 0) {
246248
// no existing results: copy over new results
@@ -252,7 +254,7 @@ private <T> Set<T> mergeResults(Set<T> results, Collection<T> newResults) {
252254
return results;
253255
}
254256

255-
// converts feature set reference to a list feature set filter
257+
/** converts feature set reference to a list feature set filter */
256258
private ListFeatureSetsRequest.Filter toListFeatureSetFilter(FeatureSetReference fsReference) {
257259
// match featuresets using contents of featureset reference
258260
String fsName = fsReference.getName();
@@ -262,16 +264,13 @@ private ListFeatureSetsRequest.Filter toListFeatureSetFilter(FeatureSetReference
262264
// for proto3, default value for missing values:
263265
// - numeric values (ie int) is zero
264266
// - strings is empty string
265-
ListFeatureSetsRequest.Filter filter =
266-
ListFeatureSetsRequest.Filter.newBuilder()
267-
.setFeatureSetName((fsName != "") ? fsName : "*")
268-
.setProject((fsProject != "") ? fsProject : "*")
269-
.build();
270-
271-
return filter;
267+
return ListFeatureSetsRequest.Filter.newBuilder()
268+
.setFeatureSetName(fsName.isEmpty() ? "*" : fsName)
269+
.setProject(fsProject.isEmpty() ? "*" : fsProject)
270+
.build();
272271
}
273272

274-
// sync job status using job manager
273+
/** sync job status using job manager */
275274
private Job syncJobStatus(JobManager jobManager, Job job) {
276275
JobStatus newStatus = jobManager.getJobStatus(job);
277276
// log job status transition

core/src/test/java/feast/core/service/JobServiceTest.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,7 @@
4646
import feast.proto.core.StoreProto.Store.StoreType;
4747
import feast.proto.types.ValueProto.ValueType.Enum;
4848
import java.time.Instant;
49-
import java.util.ArrayList;
50-
import java.util.Arrays;
51-
import java.util.Date;
52-
import java.util.List;
53-
import java.util.Optional;
49+
import java.util.*;
5450
import org.junit.Before;
5551
import org.junit.Test;
5652
import org.mockito.Mock;
@@ -87,7 +83,7 @@ public void setup() {
8783
"*:*:*");
8884

8985
// fake featureset & job
90-
this.featureSet = this.newDummyFeatureSet("food", 2, "hunger");
86+
this.featureSet = this.newDummyFeatureSet("food", "hunger");
9187
this.job = this.newDummyJob("kafka-to-redis", "job-1111", JobStatus.PENDING);
9288
try {
9389
this.ingestionJob = this.job.toProto();
@@ -140,7 +136,7 @@ public void setupJobManager() {
140136
.thenReturn(this.newDummyJob(this.job.getId(), this.job.getExtId(), JobStatus.PENDING));
141137
}
142138

143-
private FeatureSet newDummyFeatureSet(String name, int version, String project) {
139+
private FeatureSet newDummyFeatureSet(String name, String project) {
144140
Feature feature = TestObjectFactory.CreateFeature(name + "_feature", Enum.INT64);
145141
Entity entity = TestObjectFactory.CreateEntity(name + "_entity", Enum.STRING);
146142

@@ -242,7 +238,7 @@ public void testListJobsByStoreName() {
242238

243239
@Test
244240
public void testListIngestionJobByFeatureSetReference() {
245-
// list job by feature set reference: name and version and project
241+
// list job by feature set reference: name and project
246242
ListIngestionJobsRequest.Filter filter =
247243
ListIngestionJobsRequest.Filter.newBuilder()
248244
.setFeatureSetReference(this.fsReferences.get(0))
@@ -281,7 +277,7 @@ private StopIngestionJobResponse tryStopJob(
281277
fail("Expected exception, but none was thrown");
282278
}
283279
} catch (Exception e) {
284-
if (expectError != true) {
280+
if (!expectError) {
285281
// unexpected exception
286282
e.printStackTrace();
287283
fail("Caught Unexpected exception trying to restart job");
@@ -330,8 +326,7 @@ public void testStopUnsupportedError() {
330326
// check for UnsupportedOperationException when trying to stop jobs are
331327
// in an in unknown or in a transitional state
332328
JobStatus prevStatus = this.job.getStatus();
333-
List<JobStatus> unsupportedStatuses = new ArrayList<>();
334-
unsupportedStatuses.addAll(JobStatus.getTransitionalStates());
329+
List<JobStatus> unsupportedStatuses = new ArrayList<>(JobStatus.getTransitionalStates());
335330
unsupportedStatuses.add(JobStatus.UNKNOWN);
336331

337332
for (JobStatus status : unsupportedStatuses) {
@@ -345,6 +340,12 @@ public void testStopUnsupportedError() {
345340
this.job.setStatus(prevStatus);
346341
}
347342

343+
@Test(expected = NoSuchElementException.class)
344+
public void testStopJobForUnknownId() {
345+
var request = StopIngestionJobRequest.newBuilder().setId("bogusJobId").build();
346+
jobService.stopJob(request);
347+
}
348+
348349
// restart jobs
349350
private RestartIngestionJobResponse tryRestartJob(
350351
RestartIngestionJobRequest request, boolean expectError) {
@@ -356,7 +357,7 @@ private RestartIngestionJobResponse tryRestartJob(
356357
fail("Expected exception, but none was thrown");
357358
}
358359
} catch (Exception e) {
359-
if (expectError != true) {
360+
if (!expectError) {
360361
// unexpected exception
361362
e.printStackTrace();
362363
fail("Caught Unexpected exception trying to stop job");
@@ -392,8 +393,7 @@ public void testRestartUnsupportedError() {
392393
// check for UnsupportedOperationException when trying to restart jobs are
393394
// in an in unknown or in a transitional state
394395
JobStatus prevStatus = this.job.getStatus();
395-
List<JobStatus> unsupportedStatuses = new ArrayList<>();
396-
unsupportedStatuses.addAll(JobStatus.getTransitionalStates());
396+
List<JobStatus> unsupportedStatuses = new ArrayList<>(JobStatus.getTransitionalStates());
397397
unsupportedStatuses.add(JobStatus.UNKNOWN);
398398

399399
for (JobStatus status : unsupportedStatuses) {

0 commit comments

Comments
 (0)