Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add models operations to twin lifecycle sample #14781

Merged
merged 8 commits into from
Sep 3, 2020

Conversation

abhipsaMisra
Copy link
Member

@abhipsaMisra abhipsaMisra commented Sep 3, 2020

Add models operations to twin lifecycle sample

@@ -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());
Copy link
Member Author

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.
Copy link
Member Author

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.

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.
Copy link
Member Author

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.

@abhipsaMisra abhipsaMisra changed the title Add models operations to twin lifecycle Add models operations to twin lifecycle sample Sep 3, 2020
.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.
Copy link
Contributor

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?

Choose a reason for hiding this comment

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

What is a latch?

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..."

Copy link
Member Author

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());

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?

Copy link
Member Author

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);

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.

Copy link
Member Author

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);

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).

Copy link
Contributor

@azabbasi azabbasi left a 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());
Copy link
Contributor

Choose a reason for hiding this comment

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

Oops

@abhipsaMisra abhipsaMisra reopened this Sep 3, 2020
@abhipsaMisra abhipsaMisra merged commit f5fbc85 into master Sep 3, 2020
@abhipsaMisra abhipsaMisra deleted the feature/adt/abmisr/twinSample branch September 3, 2020 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants