mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-21 05:50:18 +08:00
Prevent concurrent access to server information storage
Affects issues: - Possibly fixed #2254
This commit is contained in:
parent
ab0c000688
commit
b97f61d1de
@ -19,6 +19,7 @@ package com.djrapitops.plan.identification;
|
||||
import com.djrapitops.plan.delivery.webserver.Addresses;
|
||||
import com.djrapitops.plan.exceptions.EnableException;
|
||||
import com.djrapitops.plan.identification.properties.ServerProperties;
|
||||
import com.djrapitops.plan.identification.storage.AtomicServerLoader;
|
||||
import com.djrapitops.plan.identification.storage.ServerDBLoader;
|
||||
import com.djrapitops.plan.identification.storage.ServerFileLoader;
|
||||
import com.djrapitops.plan.identification.storage.ServerLoader;
|
||||
@ -70,8 +71,8 @@ public class ServerServerInfo extends ServerInfo {
|
||||
) {
|
||||
super(serverProperties);
|
||||
this.currentVersion = currentVersion;
|
||||
this.fromFile = fromFile;
|
||||
this.fromDatabase = fromDatabase;
|
||||
this.fromFile = new AtomicServerLoader(fromFile);
|
||||
this.fromDatabase = new AtomicServerLoader(fromDatabase);
|
||||
this.processing = processing;
|
||||
this.addresses = addresses;
|
||||
this.config = config;
|
||||
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of Player Analytics (Plan).
|
||||
*
|
||||
* Plan is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License v3 as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Plan is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.djrapitops.plan.identification.storage;
|
||||
|
||||
import com.djrapitops.plan.identification.Server;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class AtomicServerLoader implements ServerLoader {
|
||||
|
||||
private final ReentrantLock reentrantLock;
|
||||
private final ServerLoader original;
|
||||
|
||||
public AtomicServerLoader(ServerLoader original) {
|
||||
this.original = original;
|
||||
this.reentrantLock = new ReentrantLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Server> load(ServerUUID serverUUID) {
|
||||
try {
|
||||
reentrantLock.lock();
|
||||
return original.load(serverUUID);
|
||||
} finally {
|
||||
reentrantLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Server information) {
|
||||
try {
|
||||
reentrantLock.lock();
|
||||
original.save(information);
|
||||
} finally {
|
||||
reentrantLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.djrapitops.plan.identification.storage;
|
||||
|
||||
import com.djrapitops.plan.PlanSystem;
|
||||
import com.djrapitops.plan.identification.Server;
|
||||
import com.djrapitops.plan.identification.ServerUUID;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.testcontainers.shaded.org.awaitility.Awaitility;
|
||||
import utilities.TestConstants;
|
||||
import utilities.mocks.PluginMockComponent;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ServerFileLoaderTest {
|
||||
static PlanSystem system;
|
||||
static ServerFileLoader underTest;
|
||||
private static ServerUUID serverUUID;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp(@TempDir Path tempDir) throws Exception {
|
||||
PluginMockComponent mockComponent = new PluginMockComponent(tempDir);
|
||||
system = mockComponent.getPlanSystem();
|
||||
system.enable();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
if (system != null) system.disable();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUpEach() {
|
||||
underTest = new ServerFileLoader(TestConstants.VERSION, system.getPlanFiles(), system.getConfigSystem().getConfig());
|
||||
Optional<Server> loaded = underTest.load(null);
|
||||
assertTrue(loaded.isPresent());
|
||||
|
||||
if (serverUUID == null) {
|
||||
serverUUID = loaded.get().getUuid();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void runParallelLoadsAndSaves() throws InterruptedException {
|
||||
ExecutorService executorService = new ScheduledThreadPoolExecutor(6);
|
||||
|
||||
AtomicInteger runs = new AtomicInteger(1);
|
||||
int expected = 10000;
|
||||
AtomicInteger fails = new AtomicInteger(0);
|
||||
try {
|
||||
for (int i = 0; i < expected; i++) {
|
||||
executorService.submit(() -> {
|
||||
Optional<Server> load = underTest.load(null);
|
||||
if (load.isPresent()) {
|
||||
underTest.save(load.get());
|
||||
} else {
|
||||
System.out.println("Failure " + fails.incrementAndGet());
|
||||
}
|
||||
});
|
||||
}
|
||||
Awaitility.await()
|
||||
.atMost(2, TimeUnit.MINUTES)
|
||||
.until(() -> runs.get() >= expected);
|
||||
} finally {
|
||||
executorService.shutdown();
|
||||
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
executorService.shutdownNow();
|
||||
}
|
||||
}
|
||||
assertEquals(0, fails.get());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user