From 14c7dd36aa6c6de55fcf81858aa5d0e557b58f3d Mon Sep 17 00:00:00 2001 From: Stampede Date: Mon, 5 May 2025 14:27:17 -0500 Subject: [PATCH 1/8] Implemented Jedis locking and transaction invalidation --- impactor/build.gradle.kts | 3 +- .../networking/EconomyNetworkingService.java | 48 +++++++++++++++++-- .../networking/messenger/Messenger.java | 5 ++ .../messenger/redis/RedisMessenger.java | 23 ++++----- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/impactor/build.gradle.kts b/impactor/build.gradle.kts index 49bc9cb3..ae3911b7 100644 --- a/impactor/build.gradle.kts +++ b/impactor/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { // Networking implementation("redis.clients:jedis:5.1.3") + implementation(files("libs/jedis-tool-kit-1.1.0-SNAPSHOT.jar")) testImplementation("net.kyori:adventure-text-serializer-ansi:4.14.0") testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.2") @@ -87,4 +88,4 @@ publishing { version = writeVersion(true) } } -} \ No newline at end of file +} diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index d667b3ff..88e1a074 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -25,14 +25,15 @@ package net.impactdev.impactor.core.economy.networking; +import com.google.common.collect.Maps; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.rockbb.jedis.toolkit.JedisLock; import net.impactdev.impactor.api.Impactor; import net.impactdev.impactor.api.economy.events.EconomyTransactionEvent; import net.impactdev.impactor.api.economy.events.EconomyTransferTransactionEvent; import net.impactdev.impactor.api.economy.transactions.EconomyTransaction; import net.impactdev.impactor.api.economy.transactions.EconomyTransferTransaction; -import net.impactdev.impactor.api.economy.transactions.details.EconomyTransactionType; import net.impactdev.impactor.api.logging.PluginLogger; import net.impactdev.impactor.core.economy.accounts.AccountManager; import net.impactdev.impactor.core.economy.accounts.ImpactorAccount; @@ -48,6 +49,7 @@ import net.kyori.adventure.key.Key; import org.jetbrains.annotations.NotNull; +import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -61,16 +63,35 @@ public final class EconomyNetworkingService implements MessageConsumer { private final ExpiringSet received = new ExpiringSet<>(5, TimeUnit.MINUTES); + // Holds Jedis Locks for transactions in order to release them as soon as possible + private final Map accountLocks = Maps.newHashMap(); + public EconomyNetworkingService(final BaseImpactorPlugin plugin, final AccountManager manager, final Messenger.Provider provider) { this.logger = plugin.logger(); this.manager = manager; this.messenger = provider.obtain(this); + // Lock Requests + Impactor.instance().events().subscribe(EconomyTransactionEvent.Pre.class, event -> { + if (!acquireAccountLock(event.account().owner())) event.cancelled(true); + }); + + Impactor.instance().events().subscribe(EconomyTransferTransactionEvent.Pre.class, event -> { + if (!acquireAccountLock(event.from().owner())) event.cancelled(true); + if (!acquireAccountLock(event.to().owner())) event.cancelled(true); + }); + + // Transaction Messaging Impactor.instance().events().subscribe(EconomyTransactionEvent.Post.class, event -> { + releaseAccountLock(event.account().owner()); + this.publishTransaction(event.transaction()); }); Impactor.instance().events().subscribe(EconomyTransferTransactionEvent.Post.class, event -> { + releaseAccountLock(event.from().owner()); + releaseAccountLock(event.to().owner()); + this.publishTransaction(event.transaction()); }); } @@ -156,15 +177,34 @@ private void processIncomingMessage(final @NotNull Message message) { TransactionContext context = transaction.context(); this.manager.accountIfPresent(context.account(), context.currency()) .map(x -> (ImpactorAccount) x) - .ifPresent(x -> this.manager.update(x, context.amount(), context.type())); + .ifPresent(x -> this.manager.invalidate(x.owner(), x.currency())); } else if(message instanceof TransferTransactionMessage transaction) { TransferTransactionContext context = transaction.context(); this.manager.accountIfPresent(context.from(), context.currency()) .map(x -> (ImpactorAccount) x) - .ifPresent(x -> this.manager.update(x, context.amount(), EconomyTransactionType.WITHDRAW)); + .ifPresent(x -> this.manager.invalidate(x.owner(), x.currency())); this.manager.accountIfPresent(context.to(), context.currency()) .map(x -> (ImpactorAccount) x) - .ifPresent(x -> this.manager.update(x, context.amount(), EconomyTransactionType.DEPOSIT)); + .ifPresent(x -> this.manager.invalidate(x.owner(), x.currency())); + } + } + + private boolean acquireAccountLock(UUID uuid) { + JedisLock lock = messenger.getAccountLock(uuid); + + if (lock.acquire()) { + accountLocks.put(uuid, lock); + return true; + } else { + logger.severe("Failed to acquire lock for account: " + uuid); + return false; + } + } + + private void releaseAccountLock(UUID uuid) { + JedisLock lock = accountLocks.remove(uuid); + if (lock != null) { + lock.release(); } } } diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java index d3526b88..319d283b 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java @@ -25,14 +25,19 @@ package net.impactdev.impactor.core.economy.networking.messenger; +import com.rockbb.jedis.toolkit.JedisLock; import net.impactdev.impactor.core.economy.networking.consumption.MessageConsumer; import net.impactdev.impactor.core.economy.networking.messages.Message; import org.jetbrains.annotations.NotNull; +import java.util.UUID; + public interface Messenger { void publish(final @NotNull Message message); + JedisLock getAccountLock(UUID uuid); + default void shutdown() {} interface Provider { diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java index 5f602522..505addce 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java @@ -28,7 +28,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; -import net.impactdev.impactor.api.Impactor; +import com.rockbb.jedis.toolkit.JedisLock; import net.impactdev.impactor.api.logging.PluginLogger; import net.impactdev.impactor.api.scheduler.v2.Scheduler; import net.impactdev.impactor.api.scheduler.v2.Schedulers; @@ -36,21 +36,13 @@ import net.impactdev.impactor.core.economy.networking.messages.Message; import net.impactdev.impactor.core.economy.networking.messenger.Messenger; import net.kyori.adventure.key.Key; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jetbrains.annotations.NotNull; -import redis.clients.jedis.DefaultJedisClientConfig; -import redis.clients.jedis.HostAndPort; -import redis.clients.jedis.JedisClientConfig; -import redis.clients.jedis.JedisCluster; -import redis.clients.jedis.JedisPooled; -import redis.clients.jedis.JedisPubSub; -import redis.clients.jedis.Protocol; -import redis.clients.jedis.UnifiedJedis; +import redis.clients.jedis.*; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; public final class RedisMessenger implements Messenger { @@ -118,6 +110,15 @@ public void publish(@NotNull Message message) { this.jedis.publish(this.channel.asString(), GSON.toJson(message.serialized())); } + @Override + public JedisLock getAccountLock(UUID uuid) { + return switch (jedis) { + case JedisPooled pooled -> new JedisLock(pooled, uuid.toString(), 1000, 5000); + case JedisCluster cluster -> new JedisLock(cluster, uuid.toString(), 1000, 5000); + default -> throw new IllegalStateException("Unsupported Jedis type: " + jedis.getClass().getName()); + }; + } + @Override public void shutdown() { this.closing = true; From c312e0def5b357f8a58e8914a5de2de8697f0786 Mon Sep 17 00:00:00 2001 From: Stampede Date: Mon, 5 May 2025 14:43:05 -0500 Subject: [PATCH 2/8] Renamed getAccountLock to obtainLock --- .../core/economy/networking/EconomyNetworkingService.java | 2 +- .../impactor/core/economy/networking/messenger/Messenger.java | 2 +- .../core/economy/networking/messenger/redis/RedisMessenger.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index 88e1a074..e104b7ff 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -190,7 +190,7 @@ private void processIncomingMessage(final @NotNull Message message) { } private boolean acquireAccountLock(UUID uuid) { - JedisLock lock = messenger.getAccountLock(uuid); + JedisLock lock = messenger.obtainLock(uuid); if (lock.acquire()) { accountLocks.put(uuid, lock); diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java index 319d283b..603b95ab 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/Messenger.java @@ -36,7 +36,7 @@ public interface Messenger { void publish(final @NotNull Message message); - JedisLock getAccountLock(UUID uuid); + JedisLock obtainLock(UUID uuid); default void shutdown() {} diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java index 505addce..60020757 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/messenger/redis/RedisMessenger.java @@ -111,7 +111,7 @@ public void publish(@NotNull Message message) { } @Override - public JedisLock getAccountLock(UUID uuid) { + public JedisLock obtainLock(UUID uuid) { return switch (jedis) { case JedisPooled pooled -> new JedisLock(pooled, uuid.toString(), 1000, 5000); case JedisCluster cluster -> new JedisLock(cluster, uuid.toString(), 1000, 5000); From 379443a5fe9f716f4952f8323601743172e33ecc Mon Sep 17 00:00:00 2001 From: Stampede Date: Mon, 5 May 2025 14:50:06 -0500 Subject: [PATCH 3/8] Update account cache to expire after 10 Seconds --- .../impactor/core/economy/accounts/AccountManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java index f64847be..1d618ba7 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java @@ -45,7 +45,7 @@ public final class AccountManager { public AccountManager(EconomyStorage storage) { this.accounts = Caffeine.newBuilder() - .expireAfterAccess(10, TimeUnit.MINUTES) + .expireAfterAccess(10, TimeUnit.SECONDS) .buildAsync((key, executor) -> storage.account(key.currency, key.uuid, builder -> builder)); } From f73e8b6d46a7826e8cf4a92000980732fe9b4065 Mon Sep 17 00:00:00 2001 From: Stampede Date: Mon, 5 May 2025 14:56:36 -0500 Subject: [PATCH 4/8] Release locks on account expire --- .../impactor/core/economy/accounts/AccountManager.java | 8 ++++++++ .../core/economy/networking/EconomyNetworkingService.java | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java index 1d618ba7..74161675 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java @@ -27,9 +27,12 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache; import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; +import net.impactdev.impactor.api.economy.EconomyService; import net.impactdev.impactor.api.economy.accounts.Account; import net.impactdev.impactor.api.economy.currency.Currency; import net.impactdev.impactor.api.economy.transactions.details.EconomyTransactionType; +import net.impactdev.impactor.core.economy.ImpactorEconomyService; import net.impactdev.impactor.core.economy.storage.EconomyStorage; import org.jetbrains.annotations.Nullable; @@ -46,6 +49,11 @@ public final class AccountManager { public AccountManager(EconomyStorage storage) { this.accounts = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.SECONDS) + .evictionListener((AccountKey key, Account account, RemovalCause cause) -> { + if (key != null && EconomyService.instance() instanceof ImpactorEconomyService impactor) { + impactor.networking().releaseAccountLock(key.uuid); + } + }) .buildAsync((key, executor) -> storage.account(key.currency, key.uuid, builder -> builder)); } diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index e104b7ff..28e21b1e 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -189,7 +189,7 @@ private void processIncomingMessage(final @NotNull Message message) { } } - private boolean acquireAccountLock(UUID uuid) { + public boolean acquireAccountLock(UUID uuid) { JedisLock lock = messenger.obtainLock(uuid); if (lock.acquire()) { @@ -201,7 +201,7 @@ private boolean acquireAccountLock(UUID uuid) { } } - private void releaseAccountLock(UUID uuid) { + public void releaseAccountLock(UUID uuid) { JedisLock lock = accountLocks.remove(uuid); if (lock != null) { lock.release(); From 1cebd62b7dcd024628bd60bac109ce7a307e5b1c Mon Sep 17 00:00:00 2001 From: Stampede Date: Mon, 5 May 2025 16:11:50 -0500 Subject: [PATCH 5/8] Make accountLocks map a cache --- .../core/economy/accounts/AccountManager.java | 10 +-------- .../networking/EconomyNetworkingService.java | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java index 74161675..f64847be 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java @@ -27,12 +27,9 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache; import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.RemovalCause; -import net.impactdev.impactor.api.economy.EconomyService; import net.impactdev.impactor.api.economy.accounts.Account; import net.impactdev.impactor.api.economy.currency.Currency; import net.impactdev.impactor.api.economy.transactions.details.EconomyTransactionType; -import net.impactdev.impactor.core.economy.ImpactorEconomyService; import net.impactdev.impactor.core.economy.storage.EconomyStorage; import org.jetbrains.annotations.Nullable; @@ -48,12 +45,7 @@ public final class AccountManager { public AccountManager(EconomyStorage storage) { this.accounts = Caffeine.newBuilder() - .expireAfterAccess(10, TimeUnit.SECONDS) - .evictionListener((AccountKey key, Account account, RemovalCause cause) -> { - if (key != null && EconomyService.instance() instanceof ImpactorEconomyService impactor) { - impactor.networking().releaseAccountLock(key.uuid); - } - }) + .expireAfterAccess(10, TimeUnit.MINUTES) .buildAsync((key, executor) -> storage.account(key.currency, key.uuid, builder -> builder)); } diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index 28e21b1e..0aaff74f 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -25,7 +25,9 @@ package net.impactdev.impactor.core.economy.networking; -import com.google.common.collect.Maps; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.rockbb.jedis.toolkit.JedisLock; @@ -49,7 +51,6 @@ import net.kyori.adventure.key.Key; import org.jetbrains.annotations.NotNull; -import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -64,13 +65,23 @@ public final class EconomyNetworkingService implements MessageConsumer { private final ExpiringSet received = new ExpiringSet<>(5, TimeUnit.MINUTES); // Holds Jedis Locks for transactions in order to release them as soon as possible - private final Map accountLocks = Maps.newHashMap(); + private final Cache accountLocks; public EconomyNetworkingService(final BaseImpactorPlugin plugin, final AccountManager manager, final Messenger.Provider provider) { this.logger = plugin.logger(); this.manager = manager; this.messenger = provider.obtain(this); + this.accountLocks = Caffeine.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .evictionListener((UUID key, JedisLock lock, RemovalCause cause) -> { + if (lock != null) { + System.out.println("Releasing lock"); + lock.release(); + } + }) + .build(); + // Lock Requests Impactor.instance().events().subscribe(EconomyTransactionEvent.Pre.class, event -> { if (!acquireAccountLock(event.account().owner())) event.cancelled(true); @@ -202,9 +213,6 @@ public boolean acquireAccountLock(UUID uuid) { } public void releaseAccountLock(UUID uuid) { - JedisLock lock = accountLocks.remove(uuid); - if (lock != null) { - lock.release(); - } + accountLocks.invalidate(uuid); } } From 4a468bd6da5ee3130e81d10c22ec31fb702b96f6 Mon Sep 17 00:00:00 2001 From: Stampede Date: Mon, 5 May 2025 16:25:21 -0500 Subject: [PATCH 6/8] Switch to removalListener --- .../core/economy/networking/EconomyNetworkingService.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index 0aaff74f..58d43c7a 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -74,11 +74,8 @@ public EconomyNetworkingService(final BaseImpactorPlugin plugin, final AccountMa this.accountLocks = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.SECONDS) - .evictionListener((UUID key, JedisLock lock, RemovalCause cause) -> { - if (lock != null) { - System.out.println("Releasing lock"); - lock.release(); - } + .removalListener((UUID key, JedisLock lock, RemovalCause cause) -> { + if (lock != null) lock.release(); }) .build(); From 1ef18f68954dbdbf21ca09420415e711634077bc Mon Sep 17 00:00:00 2001 From: Stampede Date: Wed, 7 May 2025 08:31:55 -0500 Subject: [PATCH 7/8] Revert "Switch to removalListener" This reverts commit 4a468bd6da5ee3130e81d10c22ec31fb702b96f6. --- .../core/economy/networking/EconomyNetworkingService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index 58d43c7a..0aaff74f 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -74,8 +74,11 @@ public EconomyNetworkingService(final BaseImpactorPlugin plugin, final AccountMa this.accountLocks = Caffeine.newBuilder() .expireAfterAccess(10, TimeUnit.SECONDS) - .removalListener((UUID key, JedisLock lock, RemovalCause cause) -> { - if (lock != null) lock.release(); + .evictionListener((UUID key, JedisLock lock, RemovalCause cause) -> { + if (lock != null) { + System.out.println("Releasing lock"); + lock.release(); + } }) .build(); From 5c6f6b030b11727ada4f9cc75c84c730d333abe5 Mon Sep 17 00:00:00 2001 From: Stampede Date: Wed, 7 May 2025 08:31:57 -0500 Subject: [PATCH 8/8] Revert "Make accountLocks map a cache" This reverts commit 1cebd62b7dcd024628bd60bac109ce7a307e5b1c. --- .../core/economy/accounts/AccountManager.java | 10 ++++++++- .../networking/EconomyNetworkingService.java | 22 ++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java index f64847be..74161675 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/accounts/AccountManager.java @@ -27,9 +27,12 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache; import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.RemovalCause; +import net.impactdev.impactor.api.economy.EconomyService; import net.impactdev.impactor.api.economy.accounts.Account; import net.impactdev.impactor.api.economy.currency.Currency; import net.impactdev.impactor.api.economy.transactions.details.EconomyTransactionType; +import net.impactdev.impactor.core.economy.ImpactorEconomyService; import net.impactdev.impactor.core.economy.storage.EconomyStorage; import org.jetbrains.annotations.Nullable; @@ -45,7 +48,12 @@ public final class AccountManager { public AccountManager(EconomyStorage storage) { this.accounts = Caffeine.newBuilder() - .expireAfterAccess(10, TimeUnit.MINUTES) + .expireAfterAccess(10, TimeUnit.SECONDS) + .evictionListener((AccountKey key, Account account, RemovalCause cause) -> { + if (key != null && EconomyService.instance() instanceof ImpactorEconomyService impactor) { + impactor.networking().releaseAccountLock(key.uuid); + } + }) .buildAsync((key, executor) -> storage.account(key.currency, key.uuid, builder -> builder)); } diff --git a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java index 0aaff74f..28e21b1e 100644 --- a/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java +++ b/impactor/src/main/java/net/impactdev/impactor/core/economy/networking/EconomyNetworkingService.java @@ -25,9 +25,7 @@ package net.impactdev.impactor.core.economy.networking; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.RemovalCause; +import com.google.common.collect.Maps; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.rockbb.jedis.toolkit.JedisLock; @@ -51,6 +49,7 @@ import net.kyori.adventure.key.Key; import org.jetbrains.annotations.NotNull; +import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -65,23 +64,13 @@ public final class EconomyNetworkingService implements MessageConsumer { private final ExpiringSet received = new ExpiringSet<>(5, TimeUnit.MINUTES); // Holds Jedis Locks for transactions in order to release them as soon as possible - private final Cache accountLocks; + private final Map accountLocks = Maps.newHashMap(); public EconomyNetworkingService(final BaseImpactorPlugin plugin, final AccountManager manager, final Messenger.Provider provider) { this.logger = plugin.logger(); this.manager = manager; this.messenger = provider.obtain(this); - this.accountLocks = Caffeine.newBuilder() - .expireAfterAccess(10, TimeUnit.SECONDS) - .evictionListener((UUID key, JedisLock lock, RemovalCause cause) -> { - if (lock != null) { - System.out.println("Releasing lock"); - lock.release(); - } - }) - .build(); - // Lock Requests Impactor.instance().events().subscribe(EconomyTransactionEvent.Pre.class, event -> { if (!acquireAccountLock(event.account().owner())) event.cancelled(true); @@ -213,6 +202,9 @@ public boolean acquireAccountLock(UUID uuid) { } public void releaseAccountLock(UUID uuid) { - accountLocks.invalidate(uuid); + JedisLock lock = accountLocks.remove(uuid); + if (lock != null) { + lock.release(); + } } }