#1147 Drop SingleFilePersistenceHandler in favor of DistributedFilesPersistenceHandler

- Remove SingleFilePersistenceHandler: DistributedFilesPersistenceHandler with segment size 1 can be used instead
- Rename  SegmentFilesPersistenceHolder to DistributedFilesPersistenceHandler
- Rename SeparateFilePersistenceHandler to IndividualFilesPersistenceHandler to match LimboPersistenceType entry
- Add link to limbo page on Wiki in the settings
This commit is contained in:
ljacqu 2017-03-28 21:42:01 +02:00
parent a764598f88
commit adb007108d
10 changed files with 47 additions and 140 deletions

View File

@ -1,5 +1,5 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly --> <!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Wed Mar 22 23:10:33 CET 2017. See docs/config/config.tpl.md --> <!-- File auto-generated on Tue Mar 28 21:38:52 CEST 2017. See docs/config/config.tpl.md -->
## AuthMe Configuration ## AuthMe Configuration
The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder,
@ -163,9 +163,6 @@ settings:
teleportUnAuthedToSpawn: false teleportUnAuthedToSpawn: false
# Can unregistered players walk around? # Can unregistered players walk around?
allowMovement: false allowMovement: false
# Should not authenticated players have speed = 0?
# This will reset the fly/walk speed to default value after the login.
removeSpeed: true
# After how many seconds should players who fail to login or register # After how many seconds should players who fail to login or register
# be kicked? Set to 0 to disable. # be kicked? Set to 0 to disable.
timeout: 30 timeout: 30
@ -451,24 +448,25 @@ Security:
# such as OP status, ability to fly, and walk/fly speed. # such as OP status, ability to fly, and walk/fly speed.
# Once the user is logged in, we add back the properties we previously saved. # Once the user is logged in, we add back the properties we previously saved.
# In this section, you may define how these properties should be handled. # In this section, you may define how these properties should be handled.
# Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Limbo-players
limbo: limbo:
persistence: persistence:
# Besides storing the data in memory, you can define if/how the data should be persisted # Besides storing the data in memory, you can define if/how the data should be persisted
# on disk. This is useful in case of a server crash, so next time the server starts we can # on disk. This is useful in case of a server crash, so next time the server starts we can
# properly restore things like OP status, ability to fly, and walk/fly speed. # properly restore things like OP status, ability to fly, and walk/fly speed.
# DISABLED: no disk storage, INDIVIDUAL_FILES: each player data in its own file, # DISABLED: no disk storage,
# SINGLE_FILE: all data in one single file (only if you have a small server!) # INDIVIDUAL_FILES: each player data in its own file,
# SEGMENT_FILES: distributes players into different buckets based on their UUID. See below. # DISTRIBUTED_FILES: distributes players into different files based on their UUID, see below
type: 'INDIVIDUAL_FILES' type: 'INDIVIDUAL_FILES'
# This setting only affects SEGMENT_FILES persistence. The segment file # This setting only affects DISTRIBUTED_FILES persistence. The segment file
# persistence attempts to reduce the number of files by distributing players into various # persistence attempts to reduce the number of files by distributing players into various
# buckets based on their UUID. This setting defines into how many files the players should # buckets based on their UUID. This setting defines into how many files the players should
# be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR, # be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR,
# ONE_TWENTY for 128, TWO_FIFTY for 256. # ONE_TWENTY for 128, TWO_FIFTY for 256.
# For example, if you expect 100 non-logged in players, setting to SIXTEEN will average # For example, if you expect 100 non-logged in players, setting to SIXTEEN will average
# 6.25 players per file (100 / 16). If you set to ONE, like persistence SINGLE_FILE, only # 6.25 players per file (100 / 16). If you set to ONE, only one file will be used and the
# one file will be used. Contrary to SINGLE_FILE, it won't keep the entries in cache, which # entries will be kept in memory, reducing the number of times we read from the file.
# may deliver different results in terms of performance. # This may deliver different results in terms of performance.
# Note: if you change this setting all data will be migrated. If you have a lot of data, # Note: if you change this setting all data will be migrated. If you have a lot of data,
# change this setting only on server restart, not with /authme reload. # change this setting only on server restart, not with /authme reload.
segmentDistribution: 'SIXTEEN' segmentDistribution: 'SIXTEEN'
@ -511,4 +509,4 @@ To change settings on a running server, save your changes to config.yml and use
--- ---
This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Wed Mar 22 23:10:33 CET 2017 This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Tue Mar 28 21:38:52 CEST 2017

View File

@ -30,7 +30,7 @@ import java.util.Optional;
* Persistence handler for LimboPlayer objects by distributing the objects to store * Persistence handler for LimboPlayer objects by distributing the objects to store
* in various segments (buckets) based on the start of the player's UUID. * in various segments (buckets) based on the start of the player's UUID.
*/ */
class SegmentFilesPersistenceHolder implements LimboPersistenceHandler { class DistributedFilesPersistenceHandler implements LimboPersistenceHandler {
private static final Type LIMBO_MAP_TYPE = new TypeToken<Map<String, LimboPlayer>>(){}.getType(); private static final Type LIMBO_MAP_TYPE = new TypeToken<Map<String, LimboPlayer>>(){}.getType();
@ -39,7 +39,7 @@ class SegmentFilesPersistenceHolder implements LimboPersistenceHandler {
private final SegmentNameBuilder segmentNameBuilder; private final SegmentNameBuilder segmentNameBuilder;
@Inject @Inject
SegmentFilesPersistenceHolder(@DataFolder File dataFolder, BukkitService bukkitService, Settings settings) { DistributedFilesPersistenceHandler(@DataFolder File dataFolder, BukkitService bukkitService, Settings settings) {
cacheFolder = new File(dataFolder, "playerdata"); cacheFolder = new File(dataFolder, "playerdata");
if (!cacheFolder.exists()) { if (!cacheFolder.exists()) {
// TODO ljacqu 20170313: Create FileUtils#mkdirs // TODO ljacqu 20170313: Create FileUtils#mkdirs
@ -100,7 +100,7 @@ class SegmentFilesPersistenceHolder implements LimboPersistenceHandler {
@Override @Override
public LimboPersistenceType getType() { public LimboPersistenceType getType() {
return LimboPersistenceType.SEGMENT_FILES; return LimboPersistenceType.DISTRIBUTED_FILES;
} }
private void saveEntries(Map<String, LimboPlayer> entries, File file) { private void saveEntries(Map<String, LimboPlayer> entries, File file) {
@ -126,7 +126,11 @@ class SegmentFilesPersistenceHolder implements LimboPersistenceHandler {
private File getPlayerSegmentFile(String uuid) { private File getPlayerSegmentFile(String uuid) {
String segment = segmentNameBuilder.createSegmentName(uuid); String segment = segmentNameBuilder.createSegmentName(uuid);
return new File(cacheFolder, segment + "-limbo.json"); return getSegmentFile(segment);
}
private File getSegmentFile(String segmentId) {
return new File(cacheFolder, segmentId + "-limbo.json");
} }
/** /**
@ -167,7 +171,7 @@ class SegmentFilesPersistenceHolder implements LimboPersistenceHandler {
ConsoleLogger.info("Saving " + limbosFromOldSegments.size() + " LimboPlayers from old segments into " ConsoleLogger.info("Saving " + limbosFromOldSegments.size() + " LimboPlayers from old segments into "
+ limboBySegment.size() + " current segments"); + limboBySegment.size() + " current segments");
for (Map.Entry<String, Map<String, LimboPlayer>> entry : limboBySegment.entrySet()) { for (Map.Entry<String, Map<String, LimboPlayer>> entry : limboBySegment.entrySet()) {
File file = new File(cacheFolder, entry.getKey() + "-limbo.json"); File file = getSegmentFile(entry.getKey());
Map<String, LimboPlayer> limbosToSave = Optional.ofNullable(readLimboPlayers(file)) Map<String, LimboPlayer> limbosToSave = Optional.ofNullable(readLimboPlayers(file))
.orElseGet(HashMap::new); .orElseGet(HashMap::new);
limbosToSave.putAll(entry.getValue()); limbosToSave.putAll(entry.getValue());
@ -193,7 +197,7 @@ class SegmentFilesPersistenceHolder implements LimboPersistenceHandler {
} }
/** /**
* Deletes files from the current segmenting scheme that are empty. * Deletes segment files that are empty.
*/ */
private void deleteEmptyFiles() { private void deleteEmptyFiles() {
File[] files = listFiles(cacheFolder); File[] files = listFiles(cacheFolder);

View File

@ -19,13 +19,13 @@ import java.nio.charset.StandardCharsets;
/** /**
* Saves LimboPlayer objects as JSON into individual files. * Saves LimboPlayer objects as JSON into individual files.
*/ */
class SeparateFilePersistenceHandler implements LimboPersistenceHandler { class IndividualFilesPersistenceHandler implements LimboPersistenceHandler {
private final Gson gson; private final Gson gson;
private final File cacheDir; private final File cacheDir;
@Inject @Inject
SeparateFilePersistenceHandler(@DataFolder File dataFolder, BukkitService bukkitService) { IndividualFilesPersistenceHandler(@DataFolder File dataFolder, BukkitService bukkitService) {
cacheDir = new File(dataFolder, "playerdata"); cacheDir = new File(dataFolder, "playerdata");
if (!cacheDir.exists() && !cacheDir.isDirectory() && !cacheDir.mkdir()) { if (!cacheDir.exists() && !cacheDir.isDirectory() && !cacheDir.mkdir()) {
ConsoleLogger.warning("Failed to create playerdata directory '" + cacheDir + "'"); ConsoleLogger.warning("Failed to create playerdata directory '" + cacheDir + "'");

View File

@ -6,13 +6,10 @@ package fr.xephi.authme.data.limbo.persistence;
public enum LimboPersistenceType { public enum LimboPersistenceType {
/** Store each LimboPlayer in a separate file. */ /** Store each LimboPlayer in a separate file. */
INDIVIDUAL_FILES(SeparateFilePersistenceHandler.class), INDIVIDUAL_FILES(IndividualFilesPersistenceHandler.class),
/** Store all LimboPlayers in the same file. */ /** Store LimboPlayers distributed in a configured number of files. */
SINGLE_FILE(SingleFilePersistenceHandler.class), DISTRIBUTED_FILES(DistributedFilesPersistenceHandler.class),
/** Distribute LimboPlayers by segments into a set number of files. */
SEGMENT_FILES(SegmentFilesPersistenceHolder.class),
/** No persistence to disk. */ /** No persistence to disk. */
DISABLED(NoOpPersistenceHandler.class); DISABLED(NoOpPersistenceHandler.class);

View File

@ -3,7 +3,7 @@ package fr.xephi.authme.data.limbo.persistence;
/** /**
* Configuration for the total number of segments to use. * Configuration for the total number of segments to use.
* <p> * <p>
* The {@link SegmentFilesPersistenceHolder} reduces the number of files by assigning each UUID * The {@link DistributedFilesPersistenceHandler} reduces the number of files by assigning each UUID
* to a segment. This enum allows to define how many segments the UUIDs should be distributed in. * to a segment. This enum allows to define how many segments the UUIDs should be distributed in.
* <p> * <p>
* Segments are defined by a <b>distribution</b> and a <b>length.</b> The distribution defines * Segments are defined by a <b>distribution</b> and a <b>length.</b> The distribution defines
@ -86,7 +86,7 @@ public enum SegmentConfiguration {
} }
/** /**
* @return number of segments to which this configuration will distribute UUIDs * @return number of segments to which this configuration will distribute all UUIDs
*/ */
public int getTotalSegments() { public int getTotalSegments() {
return (int) Math.pow(distribution, length); return (int) Math.pow(distribution, length);

View File

@ -4,7 +4,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Creates segment names for {@link SegmentFilesPersistenceHolder}. * Creates segment names for {@link DistributedFilesPersistenceHandler}.
*/ */
class SegmentNameBuilder { class SegmentNameBuilder {

View File

@ -1,94 +0,0 @@
package fr.xephi.authme.data.limbo.persistence;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.limbo.LimboPlayer;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.entity.Player;
import javax.inject.Inject;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Saves all LimboPlayers in one JSON file and keeps the entries in memory.
*/
class SingleFilePersistenceHandler implements LimboPersistenceHandler {
private final File cacheFile;
private final Gson gson;
private Map<String, LimboPlayer> entries;
@Inject
SingleFilePersistenceHandler(@DataFolder File dataFolder, BukkitService bukkitService) {
cacheFile = new File(dataFolder, "limbo.json");
if (!cacheFile.exists()) {
FileUtils.create(cacheFile);
}
gson = new GsonBuilder()
.registerTypeAdapter(LimboPlayer.class, new LimboPlayerSerializer())
.registerTypeAdapter(LimboPlayer.class, new LimboPlayerDeserializer(bukkitService))
.setPrettyPrinting()
.create();
Type type = new TypeToken<ConcurrentMap<String, LimboPlayer>>(){}.getType();
try (FileReader fr = new FileReader(cacheFile)) {
entries = gson.fromJson(fr, type);
} catch (IOException e) {
ConsoleLogger.logException("Failed to read from '" + cacheFile + "':", e);
}
if (entries == null) {
entries = new ConcurrentHashMap<>();
}
}
@Override
public LimboPlayer getLimboPlayer(Player player) {
return entries.get(PlayerUtils.getUUIDorName(player));
}
@Override
public void saveLimboPlayer(Player player, LimboPlayer limbo) {
entries.put(PlayerUtils.getUUIDorName(player), limbo);
saveEntries("adding '" + player.getName() + "'");
}
@Override
public void removeLimboPlayer(Player player) {
LimboPlayer entry = entries.remove(PlayerUtils.getUUIDorName(player));
if (entry != null) {
saveEntries("removing '" + player.getName() + "'");
}
}
/**
* Saves the entries to the disk.
*
* @param action the reason for saving (for logging purposes)
*/
private void saveEntries(String action) {
try (FileWriter fw = new FileWriter(cacheFile)) {
gson.toJson(entries, fw);
} catch (IOException e) {
ConsoleLogger.logException("Failed saving JSON limbo cache after " + action, e);
}
}
@Override
public LimboPersistenceType getType() {
return LimboPersistenceType.SINGLE_FILE;
}
}

View File

@ -23,28 +23,29 @@ public final class LimboSettings implements SettingsHolder {
"Besides storing the data in memory, you can define if/how the data should be persisted", "Besides storing the data in memory, you can define if/how the data should be persisted",
"on disk. This is useful in case of a server crash, so next time the server starts we can", "on disk. This is useful in case of a server crash, so next time the server starts we can",
"properly restore things like OP status, ability to fly, and walk/fly speed.", "properly restore things like OP status, ability to fly, and walk/fly speed.",
"DISABLED: no disk storage, INDIVIDUAL_FILES: each player data in its own file,", "DISABLED: no disk storage,",
"SINGLE_FILE: all data in one single file (only if you have a small server!)", "INDIVIDUAL_FILES: each player data in its own file,",
"SEGMENT_FILES: distributes players into different buckets based on their UUID. See below." "DISTRIBUTED_FILES: distributes players into different files based on their UUID, see below"
}) })
public static final Property<LimboPersistenceType> LIMBO_PERSISTENCE_TYPE = public static final Property<LimboPersistenceType> LIMBO_PERSISTENCE_TYPE =
newProperty(LimboPersistenceType.class, "limbo.persistence.type", LimboPersistenceType.INDIVIDUAL_FILES); newProperty(LimboPersistenceType.class, "limbo.persistence.type", LimboPersistenceType.INDIVIDUAL_FILES);
@Comment({ @Comment({
"This setting only affects SEGMENT_FILES persistence. The segment file", "This setting only affects DISTRIBUTED_FILES persistence. The segment file",
"persistence attempts to reduce the number of files by distributing players into various", "persistence attempts to reduce the number of files by distributing players into various",
"buckets based on their UUID. This setting defines into how many files the players should", "buckets based on their UUID. This setting defines into how many files the players should",
"be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR,", "be distributed. Possible values: ONE, FOUR, EIGHT, SIXTEEN, THIRTY_TWO, SIXTY_FOUR,",
"ONE_TWENTY for 128, TWO_FIFTY for 256.", "ONE_TWENTY for 128, TWO_FIFTY for 256.",
"For example, if you expect 100 non-logged in players, setting to SIXTEEN will average", "For example, if you expect 100 non-logged in players, setting to SIXTEEN will average",
"6.25 players per file (100 / 16). If you set to ONE, like persistence SINGLE_FILE, only", "6.25 players per file (100 / 16). If you set to ONE, only one file will be used and the",
"one file will be used. Contrary to SINGLE_FILE, it won't keep the entries in cache, which", "entries will be kept in memory, reducing the number of times we read from the file.",
"may deliver different results in terms of performance.", "This may deliver different results in terms of performance.",
"Note: if you change this setting all data will be migrated. If you have a lot of data,", "Note: if you change this setting all data will be migrated. If you have a lot of data,",
"change this setting only on server restart, not with /authme reload." "change this setting only on server restart, not with /authme reload."
}) })
public static final Property<SegmentConfiguration> SEGMENT_DISTRIBUTION = public static final Property<SegmentConfiguration> SEGMENT_DISTRIBUTION =
newProperty(SegmentConfiguration.class, "limbo.persistence.segmentDistribution", SegmentConfiguration.SIXTEEN); newProperty(SegmentConfiguration.class, "limbo.persistence.segmentDistribution",
SegmentConfiguration.SIXTEEN);
@Comment({ @Comment({
"Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE.", "Whether the player is allowed to fly: RESTORE, ENABLE, DISABLE.",
@ -79,7 +80,8 @@ public final class LimboSettings implements SettingsHolder {
"Before a user logs in, various properties are temporarily removed from the player,", "Before a user logs in, various properties are temporarily removed from the player,",
"such as OP status, ability to fly, and walk/fly speed.", "such as OP status, ability to fly, and walk/fly speed.",
"Once the user is logged in, we add back the properties we previously saved.", "Once the user is logged in, we add back the properties we previously saved.",
"In this section, you may define how these properties should be handled." "In this section, you may define how these properties should be handled.",
"Read more at https://github.com/AuthMe/AuthMeReloaded/wiki/Limbo-players"
}; };
return ImmutableMap.of("limbo", limboExplanation); return ImmutableMap.of("limbo", limboExplanation);
} }

View File

@ -37,10 +37,10 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Test for {@link SegmentFilesPersistenceHolder}. * Test for {@link DistributedFilesPersistenceHandler}.
*/ */
@RunWith(DelayedInjectionRunner.class) @RunWith(DelayedInjectionRunner.class)
public class SegmentFilesPersistenceHolderTest { public class DistributedFilesPersistenceHandlerTest {
/** Player is in seg32-10110 and should be migrated into seg16-f. */ /** Player is in seg32-10110 and should be migrated into seg16-f. */
private static final UUID MIGRATED_UUID = fromString("f6a97c88-7c8f-c12e-4931-6206d4ca067d"); private static final UUID MIGRATED_UUID = fromString("f6a97c88-7c8f-c12e-4931-6206d4ca067d");
@ -70,7 +70,7 @@ public class SegmentFilesPersistenceHolderTest {
@InjectDelayed @InjectDelayed
private SegmentFilesPersistenceHolder persistenceHandler; private DistributedFilesPersistenceHandler persistenceHandler;
@Mock @Mock
private Settings settings; private Settings settings;

View File

@ -30,16 +30,16 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Test for {@link SeparateFilePersistenceHandler}. * Test for {@link IndividualFilesPersistenceHandler}.
*/ */
@RunWith(DelayedInjectionRunner.class) @RunWith(DelayedInjectionRunner.class)
public class SeparateFilePersistenceHandlerTest { public class IndividualFilesPersistenceHandlerTest {
private static final UUID SAMPLE_UUID = UUID.nameUUIDFromBytes("PersistenceTest".getBytes()); private static final UUID SAMPLE_UUID = UUID.nameUUIDFromBytes("PersistenceTest".getBytes());
private static final String SOURCE_FOLDER = TestHelper.PROJECT_ROOT + "data/backup/"; private static final String SOURCE_FOLDER = TestHelper.PROJECT_ROOT + "data/backup/";
@InjectDelayed @InjectDelayed
private SeparateFilePersistenceHandler handler; private IndividualFilesPersistenceHandler handler;
@Mock @Mock
private BukkitService bukkitService; private BukkitService bukkitService;