From a61c28f525c5c25e3067c31bedc0a9f64a755af6 Mon Sep 17 00:00:00 2001 From: John Blum Date: Mon, 7 Dec 2020 21:59:26 -0800 Subject: [PATCH] Refactor the Async Inline Caching Spring Geode Sample Spring Boot application (code). Change the default AEQ batch size to 25 and enable AEQ batch conflation. Synchronize the Set of players (Golfers) and List of Pairings in the GolfTournament class. Add a getPlayers() method to the GolfTournament class returning the Golfers registered to play in the GolfTournament. Simply the isFinished() method in the GolfTournament class. Add the in(:Golfer) and signScorecard() methods to the GolfTournament.Pairing class. Rename the GolfTournamentService class to PgaTourService. Rename the GolfTournamentController class to GolferController. --- ...deAsyncInlineCachingClientApplication.java | 9 ++-- .../async/client/model/GolfTournament.java | 50 ++++++++++++------- ...namentService.java => PgaTourService.java} | 34 ++++++++----- ...tController.java => GolferController.java} | 24 +++------ .../AsyncInlineCachingConfiguration.java | 3 +- 5 files changed, 67 insertions(+), 53 deletions(-) rename spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/{GolfTournamentService.java => PgaTourService.java} (86%) rename spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/{GolfTournamentController.java => GolferController.java} (76%) diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java index ba4273d1..a7625be5 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/BootGeodeAsyncInlineCachingClientApplication.java @@ -32,7 +32,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; import example.app.caching.inline.async.client.model.GolfTournament; import example.app.caching.inline.async.client.model.support.GolfCourseBuilder; import example.app.caching.inline.async.client.model.support.GolferBuilder; -import example.app.caching.inline.async.client.service.GolfTournamentService; +import example.app.caching.inline.async.client.service.PgaTourService; import example.app.caching.inline.async.config.AsyncInlineCachingConfiguration; import example.app.caching.inline.async.config.AsyncInlineCachingRegionConfiguration; @@ -55,8 +55,9 @@ import example.app.caching.inline.async.config.AsyncInlineCachingRegionConfigura * @see example.app.caching.inline.async.client.model.GolfCourse * @see example.app.caching.inline.async.client.model.GolfTournament * @see example.app.caching.inline.async.client.model.Golfer - * @see example.app.caching.inline.async.client.service.GolfTournamentService + * @see example.app.caching.inline.async.client.service.PgaTourService * @see example.app.caching.inline.async.config.AsyncInlineCachingConfiguration + * @see example.app.caching.inline.async.config.AsyncInlineCachingRegionConfiguration * @since 1.4.0 */ @SpringBootApplication @@ -74,7 +75,7 @@ public class BootGeodeAsyncInlineCachingClientApplication { static class GolfApplicationConfiguration { @Bean - ApplicationRunner runGolfTournament(GolfTournamentService golfTournamentService) { + ApplicationRunner runGolfTournament(PgaTourService pgaTourService) { return args -> { @@ -84,7 +85,7 @@ public class BootGeodeAsyncInlineCachingClientApplication { .buildPairings() .play(); - golfTournamentService.manage(golfTournament); + pgaTourService.manage(golfTournament); }; } diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java index 764bd52c..65a01efd 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/model/GolfTournament.java @@ -23,12 +23,14 @@ import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.StreamSupport; import org.springframework.data.gemfire.util.ArrayUtils; import org.springframework.data.gemfire.util.CollectionUtils; import org.springframework.util.Assert; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -53,31 +55,23 @@ public class GolfTournament implements Iterable { private GolfCourse golfCourse; - private final List pairings = new ArrayList<>(); + private final List pairings = Collections.synchronizedList(new ArrayList<>()); - private final Set players = new HashSet<>(); + private final Set players = Collections.synchronizedSet(new HashSet<>()); - @Override - public Iterator iterator() { - return Collections.unmodifiableList(this.pairings).iterator(); + public Iterable getPlayers() { + return Collections.unmodifiableSet(this.players); } public boolean isFinished() { - Set finishedPairings = new HashSet<>(this.pairings.size()); - for (Pairing pairing : this) { if (pairing.getHole() < 18) { return false; } - else { - finishedPairings.add(pairing); - } } - this.pairings.removeAll(finishedPairings); - - return this.pairings.isEmpty(); + return true; } public GolfTournament at(GolfCourse golfCourse) { @@ -109,6 +103,11 @@ public class GolfTournament implements Iterable { return this; } + @Override + public Iterator iterator() { + return Collections.unmodifiableList(this.pairings).iterator(); + } + public GolfTournament play() { Assert.state(this.golfCourse != null, "No golf course was declared"); @@ -134,26 +133,39 @@ public class GolfTournament implements Iterable { @Getter @ToString + @EqualsAndHashCode @RequiredArgsConstructor(staticName = "of") public static class Pairing { + private final AtomicBoolean signedScorecard = new AtomicBoolean(false); + @NonNull private final Golfer playerOne; @NonNull private final Golfer playerTwo; - public int getHole() { - return getPlayerOne().getHole(); - } - - public void setHole(int hole) { + public synchronized void setHole(int hole) { this.playerOne.setHole(hole); this.playerTwo.setHole(hole); } - public int playNextHole() { + public synchronized int getHole() { + return getPlayerOne().getHole(); + } + + public boolean in(@NonNull Golfer golfer) { + return this.playerOne.equals(golfer) || this.playerTwo.equals(golfer); + } + + public synchronized int nextHole() { return getHole() + 1; } + + public synchronized boolean signScorecard() { + + return getHole() >= 18 + && this.signedScorecard.compareAndSet(false, true); + } } } diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolfTournamentService.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java similarity index 86% rename from spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolfTournamentService.java rename to spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java index f8786773..1486c2a2 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/GolfTournamentService.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/service/PgaTourService.java @@ -22,6 +22,8 @@ import java.util.Random; import java.util.Set; import java.util.function.Function; +import javax.annotation.PreDestroy; + import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.scheduling.annotation.Scheduled; @@ -45,7 +47,7 @@ import example.app.caching.inline.async.client.model.Golfer; */ @Service @SuppressWarnings("unused") -public class GolfTournamentService implements Closeable { +public class PgaTourService implements Closeable { protected static final int SCORE_DELTA_BOUND = 2; @@ -55,7 +57,7 @@ public class GolfTournamentService implements Closeable { private final Random random = new Random(System.currentTimeMillis()); - public GolfTournamentService(GolferService golferService) { + public PgaTourService(GolferService golferService) { Assert.notNull(golferService, "GolferService must not be null"); @@ -66,12 +68,23 @@ public class GolfTournamentService implements Closeable { return Optional.ofNullable(this.golfTournament); } - @Override + @Override @PreDestroy public void close() { this.golfTournament = null; } - public GolfTournamentService manage(GolfTournament golfTournament) { + public boolean isFinished() { + + GolfTournament golfTournament = this.golfTournament; + + return golfTournament == null || golfTournament.isFinished(); + } + + public boolean isNotFinished() { + return !isFinished(); + } + + public PgaTourService manage(GolfTournament golfTournament) { GolfTournament currentGolfTournament = this.golfTournament; @@ -90,13 +103,13 @@ public class GolfTournamentService implements Closeable { GolfTournament golfTournament = this.golfTournament; - if (golfTournament != null) { + if (isNotFinished()) { playHole(golfTournament); finish(golfTournament); } } - private void playHole(@NonNull GolfTournament golfTournament) { + private synchronized void playHole(@NonNull GolfTournament golfTournament) { GolfCourse golfCourse = golfTournament.getGolfCourse(); @@ -104,7 +117,7 @@ public class GolfTournamentService implements Closeable { for (GolfTournament.Pairing pairing : golfTournament) { - int hole = pairing.playNextHole(); + int hole = pairing.nextHole(); if (!occupiedHoles.contains(hole)) { if (golfCourse.isValidHoleNumber(hole)) { @@ -150,11 +163,8 @@ public class GolfTournamentService implements Closeable { private void finish(@NonNull GolfTournament golfTournament) { - if (golfTournament.isFinished()) { - - GolfCourse golfCourse = golfTournament.getGolfCourse(); - - for (GolfTournament.Pairing pairing : golfTournament) { + for (GolfTournament.Pairing pairing : golfTournament) { + if (pairing.signScorecard()) { updateScore(this::calculateFinalScore, pairing.getPlayerOne()); updateScore(this::calculateFinalScore, pairing.getPlayerTwo()); } diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolfTournamentController.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java similarity index 76% rename from spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolfTournamentController.java rename to spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java index 0be02968..e7054350 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolfTournamentController.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/client/web/GolferController.java @@ -25,43 +25,33 @@ import org.springframework.web.bind.annotation.RestController; import example.app.caching.inline.async.client.model.GolfTournament; import example.app.caching.inline.async.client.model.Golfer; -import example.app.caching.inline.async.client.service.GolfTournamentService; import example.app.caching.inline.async.client.service.GolferService; /** - * Spring Web MVC {@link RestController} used to present a view of a {@link GolfTournament} running. + * Spring Web MVC {@link RestController} used to present a view of {@link Golfer Golfers} standings + * when playing in a {@link GolfTournament} on the {@literal PGA TOUR}. * * @author John Blum - * @see example.app.caching.inline.async.client.model.Golfer - * @see example.app.caching.inline.async.client.model.GolfTournament - * @see example.app.caching.inline.async.client.service.GolferService - * @see example.app.caching.inline.async.client.service.GolfTournamentService * @see org.springframework.web.bind.annotation.GetMapping * @see org.springframework.web.bind.annotation.RequestMapping * @see org.springframework.web.bind.annotation.RestController + * @see example.app.caching.inline.async.client.model.Golfer + * @see example.app.caching.inline.async.client.model.GolfTournament + * @see example.app.caching.inline.async.client.service.GolferService * @since 1.4.0 */ @RestController @RequestMapping("/golf/tournament") @SuppressWarnings("unused") -public class GolfTournamentController { +public class GolferController { private final GolferService golferService; - private final GolfTournamentService golfTournamentService; - - public GolfTournamentController(@NonNull GolferService golferService, - @NonNull GolfTournamentService golfTournamentService) { + public GolferController(@NonNull GolferService golferService) { Assert.notNull(golferService, "GolferService must not be null"); - Assert.notNull(golfTournamentService, "GolfTournamentService must not be null"); this.golferService = golferService; - this.golfTournamentService = golfTournamentService; - } - - protected @NonNull GolfTournamentService getGolfTournamentService() { - return this.golfTournamentService; } protected @NonNull GolferService getGolferService() { diff --git a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java index 365a5d5d..725cc380 100644 --- a/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java +++ b/spring-geode-samples/caching/inline-async/src/main/java/example/app/caching/inline/async/config/AsyncInlineCachingConfiguration.java @@ -48,10 +48,11 @@ public class AsyncInlineCachingConfiguration { @Bean @Profile("queue-batch-size") AsyncInlineCachingRegionConfigurer batchSizeAsyncInlineCachingConfigurer( - @Value("${spring.geode.sample.async-inline-caching.queue.batch-size:4}") int queueBatchSize, + @Value("${spring.geode.sample.async-inline-caching.queue.batch-size:25}") int queueBatchSize, GolferRepository golferRepository) { return AsyncInlineCachingRegionConfigurer.create(golferRepository, GOLFERS_REGION_NAME) + .withQueueBatchConflationEnabled() .withQueueBatchSize(queueBatchSize) .withQueueBatchTimeInterval(Duration.ofMinutes(60)) .withQueueDispatcherThreadCount(1);