-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add models operations to twin lifecycle sample #14781
Conversation
@@ -909,7 +909,7 @@ public HttpPipeline getHttpPipeline() { | |||
@ServiceMethod(returns = ReturnType.SINGLE) | |||
public Mono<Void> deleteModel(String modelId) { | |||
return deleteModelWithResponse(modelId) | |||
.map(Response::getValue); | |||
.flatMap(voidResponse -> Mono.empty()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.map(Response::getValue)
returns null, which is not a valid return item from a Mono. So we need to map it to Mono.empty()
instead.
Semaphore deleteRelationshipsSemaphore = new Semaphore(0); | ||
CountDownLatch deleteTwinsLatch = new CountDownLatch(1); | ||
|
||
// Call APIs to retrieve all relationships. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation is very long and verbose. I am trying to see how I could chain the list and delete operations together, I'll put up an update in the next PR.
e65c336
to
48c3c37
Compare
List<String> models = asList(RoomModelId, WifiModelId, BuildingModelId, FloorModelId, HvacModelId); | ||
|
||
// Call APIs to delete the models. | ||
// Note that we are blocking the async API call. This is to ensure models are deleted in an order such that no other models are referencing it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have been able to schedule the async API on a single thread, and forced them to run sequentially; however, I didn't have success with that.
I am blocking the async API call for now, until I figure that out.
.doOnTerminate(deleteTwinsLatch::countDown) | ||
.subscribe(); | ||
|
||
// Wait until the latch has been counted down for each async delete operation, signifying that the async call has completed successfully. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For each async delete? Can you elaborate what you mean here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a latch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appears to be some kind of threadsafe counter, eh?
I'd reword this to be "Wait until the latch count reaches zero..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A countdown latch is a mechanism to block the calling thread until other threads that are running in parallel have completed (counted down). The .countdown()
essentially decrements a thread-safe counter.
From this article online: https://www.baeldung.com/java-countdown-latch
Simply put, a CountDownLatch has a counter field, which you can decrement as we require. We can then use it to block a calling thread until it's been counted down to zero.
If we were doing some parallel processing, we could instantiate the CountDownLatch with the same value for the counter as a number of threads we want to work across. Then, we could just call countdown() after each thread finishes, guaranteeing that a dependent thread calling await() will block until the worker threads are finished.
.forEach(relationship -> client.deleteRelationship(relationship.getSourceId(), relationship.getId()) | ||
.doOnSuccess(aVoid -> { | ||
if (twinId.equals(relationship.getSourceId())) { | ||
System.out.println("Found and deleted relationship: " + relationship.getId()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does Java support string interpolation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can use String.format()
to specify a template, but nothing as handy as $
in C#.
List<BasicRelationship> relationshipList = new ArrayList<>(); | ||
Semaphore listRelationshipSemaphore = new Semaphore(0); | ||
Semaphore deleteRelationshipsSemaphore = new Semaphore(0); | ||
CountDownLatch deleteTwinsLatch = new CountDownLatch(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunate that you have to know ahead of time how many items you are counting down. This presents a "magic" number, which can be confusing to someone else reviewing the code who doesn't realize the significance (this will be used to asynchronously delete 1 dt).
Also, in C# we have a general rule of not declaring a variable until it is needed (so it is more contextual). Does Java have a different standard of all variables at the top?
FWIW, I think this could at least use a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to initialize these latches and semaphore within the scope that they are referenced in; but yes, I agree, the count can be confusing to understand.
I'll add some more comments around this.
System.out.println("Deleted model: " + modelId); | ||
} catch (ErrorResponseException ex) { | ||
if (ex.getResponse().getStatusCode() != HttpStatus.SC_NOT_FOUND) { | ||
System.err.println("Could not delete model " + modelId + " due to " + ex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If Java doesn't have string interpolation, I'd think this would be much more readable as a string format (like below).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this sample is doing way too much work. Reconsider making it just creating a brand new model with brand new id and create a twin off of that and then clean up after itself.
That would make it concise and easier to navigate.
@@ -788,7 +788,7 @@ public HttpPipeline getHttpPipeline() { | |||
objectPagedResponse.getHeaders(), | |||
convertedList, | |||
null, | |||
((PagedResponseBase) objectPagedResponse).getDeserializedHeaders()); | |||
((ResponseBase) objectPagedResponse).getDeserializedHeaders()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops
Add models operations to twin lifecycle sample