-
-
Notifications
You must be signed in to change notification settings - Fork 84
Add platform-neutral shared vote-processing core #1608
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
Open
BenCodez
wants to merge
19
commits into
master
Choose a base branch
from
codex/shared-vote-processing-core
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
555cabe
Add platform-neutral vote input
BenCodez 2396efc
Add shared vote identity
BenCodez 7c74535
Add shared vote identity resolver
BenCodez 83305bf
Add shared vote persistence mutation
BenCodez ea53c5c
Add shared vote user snapshot
BenCodez d0c185c
Add AdvancedCore-facing vote user services port
BenCodez 6bed0f0
Add AdvancedCore-facing reward services port
BenCodez e57adc8
Add shared vote processing policy
BenCodez 3047c9f
Add shared vote processing result
BenCodez 429e624
Add platform-neutral vote processor
BenCodez f4d39a7
Test headless vote persistence and rewards across restart
BenCodez e231f62
Make shared vote persistence and reward handoff recoverable by vote ID
BenCodez 0a0a7eb
Use live online state for proxy vote totals
BenCodez 64697cb
Persist normalized vote time and prepared reward version
BenCodez 5f25641
Preserve vote sentinel and proxy routing semantics
BenCodez b38e510
Trust atomic vote receipt winner
BenCodez 24a8dca
Model atomic sentinel and proxy-route persistence
BenCodez de49ab1
Cover concurrent vote receipt races
BenCodez 4949ac9
Exercise atomic vote receipt concurrency
BenCodez File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteIdentity.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
|
|
||
| public record SharedVoteIdentity(UUID uuid, String playerName, boolean online) { | ||
| public SharedVoteIdentity { | ||
| Objects.requireNonNull(uuid, "uuid"); | ||
| Objects.requireNonNull(playerName, "playerName"); | ||
| if (playerName.isBlank()) { | ||
| throw new IllegalArgumentException("playerName cannot be blank"); | ||
| } | ||
| } | ||
| } |
9 changes: 9 additions & 0 deletions
9
...gPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteIdentityResolver.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.concurrent.CompletionStage; | ||
|
|
||
| /** Adapter to the existing AdvancedCore/user identity services. */ | ||
| @FunctionalInterface | ||
| public interface SharedVoteIdentityResolver { | ||
| CompletionStage<SharedVoteIdentity> resolve(SharedVoteInput input); | ||
| } |
54 changes: 54 additions & 0 deletions
54
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteInput.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
|
|
||
| /** | ||
| * An already-accepted vote entering the shared processing path. Native ingress, | ||
| * proxy/global duplicate filtering, service-site validation and security checks | ||
| * remain upstream and are intentionally not reimplemented here. | ||
| */ | ||
| public record SharedVoteInput(UUID voteId, String playerName, String serviceSite, long voteTime, | ||
| boolean realVote, boolean addTotals, boolean proxyVote, boolean forceProxyRouting, boolean wasOnline) { | ||
| public SharedVoteInput { | ||
| Objects.requireNonNull(voteId, "voteId"); | ||
| Objects.requireNonNull(playerName, "playerName"); | ||
| Objects.requireNonNull(serviceSite, "serviceSite"); | ||
| if (playerName.isBlank()) throw new IllegalArgumentException("playerName cannot be blank"); | ||
| if (serviceSite.isBlank()) throw new IllegalArgumentException("serviceSite cannot be blank"); | ||
| if (voteTime < 0) throw new IllegalArgumentException("voteTime cannot be negative"); | ||
| } | ||
|
|
||
| /** | ||
| * Compatibility constructor for callers that historically had one proxy bit. | ||
| * Native adapters should use the full constructor and map isBungee() and | ||
| * isForceBungee() independently. | ||
| */ | ||
| public SharedVoteInput(UUID voteId, String playerName, String serviceSite, long voteTime, | ||
| boolean realVote, boolean addTotals, boolean proxyVote, boolean wasOnline) { | ||
| this(voteId, playerName, serviceSite, voteTime, realVote, addTotals, | ||
| proxyVote, proxyVote, wasOnline); | ||
| } | ||
|
|
||
| /** PlayerVoteEvent uses zero as "now"; normalize before durable mutation/receipt creation. */ | ||
| public SharedVoteInput normalizedVoteTime(long nowEpochMillis) { | ||
| if (voteTime != 0) return this; | ||
| if (nowEpochMillis <= 0) throw new IllegalArgumentException("normalized vote time must be positive"); | ||
| return new SharedVoteInput(voteId, playerName, serviceSite, nowEpochMillis, | ||
| realVote, addTotals, proxyVote, forceProxyRouting, wasOnline); | ||
| } | ||
|
|
||
| /** A retry carrying the zero sentinel still identifies its already-normalized persisted receipt. */ | ||
| public boolean matchesPersisted(SharedVoteInput persisted) { | ||
| if (persisted == null) return false; | ||
| return voteId.equals(persisted.voteId()) | ||
| && playerName.equals(persisted.playerName()) | ||
| && serviceSite.equals(persisted.serviceSite()) | ||
| && (voteTime == 0 || voteTime == persisted.voteTime()) | ||
| && realVote == persisted.realVote() | ||
| && addTotals == persisted.addTotals() | ||
| && proxyVote == persisted.proxyVote() | ||
| && forceProxyRouting == persisted.forceProxyRouting() | ||
| && wasOnline == persisted.wasOnline(); | ||
| } | ||
| } |
16 changes: 16 additions & 0 deletions
16
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteMutation.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
|
|
||
| /** | ||
| * Describes the logical vote mutation. The AdvancedCore-facing storage adapter | ||
| * owns actual keys, cache/queue ordering, point hooks/caps, and persistence. | ||
| */ | ||
| public record SharedVoteMutation(UUID voteId, String serviceSite, long voteTime, | ||
| boolean countTotals, boolean awardConfiguredPoints) { | ||
| public SharedVoteMutation { | ||
| Objects.requireNonNull(voteId, "voteId"); | ||
| Objects.requireNonNull(serviceSite, "serviceSite"); | ||
| } | ||
| } |
29 changes: 29 additions & 0 deletions
29
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVotePolicy.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| /** | ||
| * Platform-neutral subset of the existing vote-processing configuration. | ||
| * Native adapters should populate it from the current VotingPlugin config. | ||
| */ | ||
| public record SharedVotePolicy(boolean countFakeVotes, boolean addTotals, | ||
| boolean addTotalsOffline, boolean processRewards, boolean giveOfflineRewards) { | ||
|
|
||
| boolean shouldApplyConfiguredVoteMutation(SharedVoteInput input) { | ||
| return input.addTotals() && (input.realVote() || countFakeVotes); | ||
| } | ||
|
|
||
| boolean shouldCountTotals(SharedVoteInput input, boolean online) { | ||
| return shouldApplyConfiguredVoteMutation(input) && addTotals && (addTotalsOffline || online); | ||
| } | ||
|
|
||
| boolean shouldAwardConfiguredPoints(SharedVoteInput input) { | ||
| // Existing Bukkit behavior awards configured vote points when the accepted | ||
| // vote is countable even if Config.AddTotals itself is disabled. | ||
| return shouldApplyConfiguredVoteMutation(input); | ||
| } | ||
|
|
||
| boolean shouldExecuteRewardsNow(SharedVoteInput input, boolean online) { | ||
| // Proxy votes preserve the existing force-processing behavior. For native | ||
| // votes, ProcessRewards and per-site offline eligibility remain authoritative. | ||
| return input.proxyVote() || (processRewards && (online || giveOfflineRewards)); | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
...gPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteProcessingResult.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| public record SharedVoteProcessingResult(SharedVoteIdentity identity, SharedVoteUserSnapshot persistedState, | ||
| RewardDisposition rewardDisposition) { | ||
| public SharedVoteProcessingResult { | ||
| Objects.requireNonNull(identity, "identity"); | ||
| Objects.requireNonNull(persistedState, "persistedState"); | ||
| Objects.requireNonNull(rewardDisposition, "rewardDisposition"); | ||
| } | ||
|
|
||
| public enum RewardDisposition { | ||
| EXECUTED, | ||
| DEFERRED | ||
| } | ||
| } |
129 changes: 129 additions & 0 deletions
129
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteProcessor.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.concurrent.CompletionStage; | ||
| import java.util.function.Supplier; | ||
|
|
||
| import com.bencodez.votingplugin.core.vote.SharedVoteProcessingResult.RewardDisposition; | ||
|
|
||
| public final class SharedVoteProcessor { | ||
| private static final int MAX_RECOVERY_BATCH = 100; | ||
| private final SharedVoteIdentityResolver identities; | ||
| private final SharedVoteUserServices users; | ||
| private final SharedVoteRewardServices rewards; | ||
|
|
||
| public SharedVoteProcessor(SharedVoteIdentityResolver identities, SharedVoteUserServices users, | ||
| SharedVoteRewardServices rewards) { | ||
| this.identities = Objects.requireNonNull(identities, "identities"); | ||
| this.users = Objects.requireNonNull(users, "users"); | ||
| this.rewards = Objects.requireNonNull(rewards, "rewards"); | ||
| } | ||
|
|
||
| public CompletionStage<SharedVoteProcessingResult> process(SharedVoteInput input, SharedVotePolicy policy) { | ||
| Objects.requireNonNull(input, "input"); | ||
| Objects.requireNonNull(policy, "policy"); | ||
| return call(() -> users.findVote(input.voteId()), "receipt lookup").thenCompose(existing -> { | ||
| if (existing != null) { | ||
| existing.requireInput(input); | ||
| return deliver(existing); | ||
| } | ||
| SharedVoteInput normalized = input.normalizedVoteTime(System.currentTimeMillis()); | ||
| return call(() -> identities.resolve(normalized), "identity resolution").thenCompose(identity -> { | ||
| if (identity == null) return failed("Identity resolver returned null identity"); | ||
| boolean currentOnline = identity.online(); | ||
| boolean rewardOnline = normalized.proxyVote() ? normalized.wasOnline() : currentOnline; | ||
| SharedVoteMutation mutation = new SharedVoteMutation(normalized.voteId(), normalized.serviceSite(), | ||
| normalized.voteTime(), policy.shouldCountTotals(normalized, currentOnline), | ||
| policy.shouldAwardConfiguredPoints(normalized)); | ||
| boolean executeNow = policy.shouldExecuteRewardsNow(normalized, rewardOnline); | ||
| return call(() -> rewards.prepareVoteRewards(normalized, identity, executeNow), "reward preparation") | ||
| .thenCompose(rewardPlan -> { | ||
| if (rewardPlan == null) return failed("Reward services returned null prepared reward plan"); | ||
| // Preserve the ingress sentinel for the atomic uniqueness check. The | ||
| // mutation carries this caller's normalized candidate; if another | ||
| // concurrent caller wins, the returned receipt is authoritative. | ||
| return call(() -> users.persistVoteWithReward(input, identity, mutation, executeNow, rewardPlan), | ||
| "atomic persistence").thenCompose(receipt -> { | ||
| if (receipt == null) return failed("User services returned null vote receipt"); | ||
| receipt.requireInput(input); | ||
| if (!receipt.identity().uuid().equals(identity.uuid())) { | ||
| return failed("Vote receipt belongs to a different resolved identity"); | ||
| } | ||
| return deliver(receipt); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| public CompletionStage<SharedVoteProcessingResult> recover(UUID voteId) { | ||
| Objects.requireNonNull(voteId, "voteId"); | ||
| return call(() -> users.findVote(voteId), "receipt lookup").thenCompose(receipt -> { | ||
| if (receipt == null || !voteId.equals(receipt.input().voteId())) return failed("Pending vote receipt was not found"); | ||
| return deliver(receipt); | ||
| }); | ||
| } | ||
|
|
||
| public CompletionStage<List<SharedVoteProcessingResult>> recoverPending(int limit) { | ||
| if (limit < 1 || limit > MAX_RECOVERY_BATCH) throw new IllegalArgumentException("Recovery limit must be 1..100"); | ||
| return call(() -> users.pendingVotes(limit), "pending receipt scan").thenCompose(receipts -> { | ||
| if (receipts == null || receipts.size() > limit) return failed("Invalid pending receipt batch"); | ||
| List<SharedVoteReceipt> batch = List.copyOf(receipts); | ||
| HashSet<UUID> ids = new HashSet<>(); | ||
| for (SharedVoteReceipt receipt : batch) { | ||
| if (!receipt.pending() || !ids.add(receipt.input().voteId())) return failed("Invalid pending receipt entry"); | ||
| } | ||
| List<SharedVoteProcessingResult> results = new ArrayList<>(); | ||
| List<Throwable> failures = new ArrayList<>(); | ||
| CompletionStage<Void> chain = CompletableFuture.completedFuture(null); | ||
| for (SharedVoteReceipt receipt : batch) { | ||
| chain = chain.thenCompose(ignored -> deliver(receipt).handle((result, failure) -> { | ||
| if (failure == null) results.add(result); else failures.add(failure); | ||
| return null; | ||
| })); | ||
| } | ||
| return chain.thenCompose(ignored -> { | ||
| if (failures.isEmpty()) return CompletableFuture.completedFuture(List.copyOf(results)); | ||
| IllegalStateException failure = new IllegalStateException("Some pending vote rewards could not be recovered"); | ||
| failures.forEach(failure::addSuppressed); | ||
| return CompletableFuture.failedFuture(failure); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| private CompletionStage<SharedVoteProcessingResult> deliver(SharedVoteReceipt receipt) { | ||
| if (!receipt.pending()) return CompletableFuture.completedFuture(result(receipt)); | ||
| return call(() -> rewards.deliverOnce(receipt), "keyed reward delivery").thenCompose(disposition -> { | ||
| if (disposition == null) return failed("Reward services returned null delivery disposition"); | ||
| return call(() -> users.markRewardCompleted(receipt.input().voteId(), disposition), "reward acknowledgement") | ||
| .thenApply(completed -> { | ||
| if (completed == null) throw new IllegalStateException("Missing acknowledged vote receipt"); | ||
| completed.requireSameOrigin(receipt); | ||
| if (completed.completedDisposition() != disposition) { | ||
| throw new IllegalStateException("Vote reward acknowledgement did not preserve the delivery result"); | ||
| } | ||
| return result(completed); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| private static SharedVoteProcessingResult result(SharedVoteReceipt receipt) { | ||
| return new SharedVoteProcessingResult(receipt.identity(), receipt.persistedState(), receipt.completedDisposition()); | ||
| } | ||
|
|
||
| private static <T> CompletionStage<T> call(Supplier<CompletionStage<T>> operation, String name) { | ||
| try { | ||
| CompletionStage<T> stage = operation.get(); | ||
| return stage == null ? failed("Adapter returned null stage for " + name) : stage; | ||
| } catch (Throwable failure) { return CompletableFuture.failedFuture(failure); } | ||
| } | ||
|
|
||
| private static <T> CompletionStage<T> failed(String message) { | ||
| return CompletableFuture.failedFuture(new IllegalStateException(message)); | ||
| } | ||
| } | ||
53 changes: 53 additions & 0 deletions
53
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteReceipt.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| import com.bencodez.votingplugin.core.vote.SharedVoteProcessingResult.RewardDisposition; | ||
|
|
||
| /** | ||
| * Immutable receipt keyed by voteId in the existing persistence owner. Its initial | ||
| * form is committed atomically with totals/points, pending reward intent and the | ||
| * immutable prepared reward version. Across retries only the acknowledgement changes. | ||
| */ | ||
| public record SharedVoteReceipt(SharedVoteInput input, SharedVoteIdentity identity, SharedVoteMutation mutation, | ||
| SharedVoteUserSnapshot persistedState, boolean executeRewardsNow, SharedVoteRewardPlan rewardPlan, | ||
| RewardDisposition completedDisposition) { | ||
| public SharedVoteReceipt { | ||
| Objects.requireNonNull(input, "input"); | ||
| Objects.requireNonNull(identity, "identity"); | ||
| Objects.requireNonNull(mutation, "mutation"); | ||
| Objects.requireNonNull(persistedState, "persistedState"); | ||
| Objects.requireNonNull(rewardPlan, "rewardPlan"); | ||
| if (input.voteTime() <= 0) throw new IllegalArgumentException("Persisted vote time must be normalized"); | ||
| if (!input.voteId().equals(mutation.voteId()) || !input.serviceSite().equals(mutation.serviceSite()) | ||
| || input.voteTime() != mutation.voteTime()) { | ||
| throw new IllegalArgumentException("Vote receipt input and mutation do not match"); | ||
| } | ||
| } | ||
|
|
||
| public boolean pending() { return completedDisposition == null; } | ||
|
|
||
| public SharedVoteReceipt completed(RewardDisposition disposition) { | ||
| Objects.requireNonNull(disposition, "disposition"); | ||
| if (!pending() && disposition != completedDisposition) { | ||
| throw new IllegalStateException("A completed vote receipt cannot change its reward result"); | ||
| } | ||
| return new SharedVoteReceipt(input, identity, mutation, persistedState, executeRewardsNow, rewardPlan, disposition); | ||
| } | ||
|
|
||
| public void requireInput(SharedVoteInput expected) { | ||
| Objects.requireNonNull(expected, "expected"); | ||
| if (!expected.matchesPersisted(input)) { | ||
| throw new IllegalStateException("voteId is already bound to different vote input"); | ||
| } | ||
| } | ||
|
|
||
| public void requireSameOrigin(SharedVoteReceipt expected) { | ||
| requireInput(expected.input()); | ||
| if (!identity.equals(expected.identity()) || !mutation.equals(expected.mutation()) | ||
| || !persistedState.equals(expected.persistedState()) || executeRewardsNow != expected.executeRewardsNow() | ||
| || !rewardPlan.equals(expected.rewardPlan())) { | ||
| throw new IllegalStateException("Vote receipt changed while acknowledging reward delivery"); | ||
| } | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteRewardPlan.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * Immutable reference to the exact reward definition prepared for one vote. | ||
| * The version reference must continue resolving to the same archived/snapshotted | ||
| * definition after configuration reloads and process restarts. | ||
| */ | ||
| public record SharedVoteRewardPlan(String planId, String versionReference) { | ||
| public SharedVoteRewardPlan { | ||
| Objects.requireNonNull(planId, "planId"); | ||
| Objects.requireNonNull(versionReference, "versionReference"); | ||
| if (planId.isBlank()) throw new IllegalArgumentException("planId cannot be blank"); | ||
| if (versionReference.isBlank()) throw new IllegalArgumentException("versionReference cannot be blank"); | ||
| } | ||
| } |
37 changes: 37 additions & 0 deletions
37
VotingPlugin/src/main/java/com/bencodez/votingplugin/core/vote/SharedVoteRewardServices.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package com.bencodez.votingplugin.core.vote; | ||
|
|
||
| import java.util.concurrent.CompletableFuture; | ||
| import java.util.concurrent.CompletionStage; | ||
|
|
||
| import com.bencodez.votingplugin.core.vote.SharedVoteProcessingResult.RewardDisposition; | ||
|
|
||
| /** Adapter for AdvancedCore reward orchestration and its existing durable replay owner. */ | ||
| public interface SharedVoteRewardServices { | ||
| CompletionStage<Void> executeVoteRewards(SharedVoteInput input, SharedVoteIdentity identity, | ||
| SharedVoteUserSnapshot persistedState); | ||
|
|
||
| CompletionStage<Void> deferVoteRewards(SharedVoteInput input, SharedVoteIdentity identity, | ||
| SharedVoteUserSnapshot persistedState); | ||
|
|
||
| /** | ||
| * Resolve and freeze the exact reward configuration BEFORE the vote transaction. | ||
| * versionReference must resolve to this same definition after reload/restart. | ||
| */ | ||
| default CompletionStage<SharedVoteRewardPlan> prepareVoteRewards(SharedVoteInput input, | ||
| SharedVoteIdentity identity, boolean executeRewardsNow) { | ||
| return CompletableFuture.failedFuture(new UnsupportedOperationException( | ||
| "Shared vote rewards require a persistable prepared reward version")); | ||
| } | ||
|
|
||
| /** | ||
| * Admit/deduplicate by receipt.input().voteId() in the existing reward owner. | ||
| * Use receipt.rewardPlan() rather than current configuration. Serialize concurrent | ||
| * delivery/recovery for that occurrence, preserve step checkpoints and durably | ||
| * remember the terminal disposition. A repeated call resumes pending work or | ||
| * returns the same result without redoing completed work. | ||
| */ | ||
| default CompletionStage<RewardDisposition> deliverOnce(SharedVoteReceipt receipt) { | ||
| return CompletableFuture.failedFuture(new UnsupportedOperationException( | ||
| "Shared vote rewards require keyed durable replay support")); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.