Skip to content
This repository has been archived by the owner on Aug 9, 2024. It is now read-only.

Commit

Permalink
migration from legacy yaml stores
Browse files Browse the repository at this point in the history
  • Loading branch information
lucyydotp committed Feb 6, 2023
1 parent 7599f14 commit 8d2445a
Show file tree
Hide file tree
Showing 19 changed files with 225 additions and 51 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.gradle/
.idea/
*.iml
.fleet/

**/build/
**/run/
4 changes: 4 additions & 0 deletions pronouns-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@ dependencies {
compileOnlyApi(libs.adventure.api)
compileOnlyApi(libs.adventure.text.minimessage)
compileOnly(libs.gson)
compileOnly("org.yaml:snakeyaml:1.33")
compileOnly(libs.hikari)

testImplementation("org.yaml:snakeyaml:1.33")

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,63 @@

import net.lucypoulton.pronouns.api.set.PronounSet;
import net.lucypoulton.pronouns.api.PronounParser;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

public class LegacyMigrator {

private LegacyMigrator() {}

private static final PronounParser parser = new PronounParser(PronounSet.builtins);

public static PronounSet fromLegacyString(String string) {
public static PronounSet fromLegacyString(final String string) {
final var split = string.split("/");
try {
return parser.parse(split[0]).get(0);
} catch (IllegalArgumentException ignored) {}
} catch (IllegalArgumentException ignored) {
}

if (split.length != 6) throw new IllegalArgumentException("Failed to parse legacy set " + string);

return PronounSet.from(
split[0],
split[1],
split[3],
split[4],
split[5],
split[2].endsWith("re") // best guess at whether it's singular or plural
split[0],
split[1],
split[3],
split[4],
split[5],
split[2].endsWith("re") // best guess at whether it's singular or plural
);
}

public static final class MigrationException extends RuntimeException {
public MigrationException(Throwable cause) {
super(cause);
}

public MigrationException(String message) {
super(message);
}
}

private static final Yaml yaml = new Yaml();

@SuppressWarnings("unchecked")
public static Map<UUID, List<PronounSet>> fromYaml(final Path path) {
try (final var file = Files.newInputStream(path)) {
final var loaded = (Map<String, String[]>) yaml.load(file);
final var out = new HashMap<UUID, List<PronounSet>>();
loaded.forEach((uuid, sets) -> out.put(UUID.fromString(uuid), Arrays.stream(sets).map(LegacyMigrator::fromLegacyString).toList()));
file.close();
Files.move(path, path.resolveSibling("legacy-datastore.yml"));
return out;
} catch (ClassCastException e) {
throw new MigrationException("Legacy datastore file is incorrectly formatted");
} catch (IOException e) {
throw new MigrationException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,22 @@ public ProNouns(Platform platform) {
platform.logger().info(ProNounsTranslations.translate("pronouns.first-launch"));
}

final var helpCommand = new HelpCommand(this, commandManager);

final var commands = List.of(
helpCommand,
new GetCommand(this, platform),
new SetCommand(this, platform),
new ClearCommand(this, platform),
new VersionCommand(this, platform),
new DebugCommand(this, platform),
new UpdateCommand(this),
new HelpCommand(this, commandManager),
new ReloadCommand(this),
new DumpCommand(this, platform)
new DumpCommand(this, platform),
new MigrateCommand(this)
);

commandManager.commandBuilder("pronouns", "pn").apply(helpCommand::buildForRoot);
for (final var command : commands) {
commandManager.command(
commandManager.commandBuilder("pronouns", "pn").apply(command::build)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ private String statistics() {

return new Gson().toJson(out);
}

public void send() {
final var req = HttpRequest.newBuilder()
.uri(ENDPOINT)
Expand All @@ -43,9 +43,7 @@ public void send() {
.POST(HttpRequest.BodyPublishers.ofString(statistics()))
.build();
try {
platform.logger().warning("sending stats");
final var res = HttpUtil.client().send(req, HttpResponse.BodyHandlers.discarding());
platform.logger().warning(String.valueOf(res.statusCode()));
HttpUtil.client().send(req, HttpResponse.BodyHandlers.discarding());
} catch (Exception ex) {
platform.logger().warning("Failed to upload statistics. Please report this!" + ex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,8 @@ public Command.Builder<CommandSender> build(Command.Builder<CommandSender> build
ctx.getOrDefault("query", null))
);
}

public Command.Builder<CommandSender> buildForRoot(Command.Builder<CommandSender> builder) {
return builder.handler(ctx -> execute(ctx.getSender(), null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package net.lucypoulton.pronouns.common.cmd;

import cloud.commandframework.Command;
import cloud.commandframework.arguments.flags.CommandFlag;
import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.context.CommandContext;
import cloud.commandframework.meta.CommandMeta;
import net.lucypoulton.pronouns.api.set.PronounSet;
import net.lucypoulton.pronouns.common.LegacyMigrator;
import net.lucypoulton.pronouns.common.ProNouns;
import net.lucypoulton.pronouns.common.message.ProNounsTranslations;
import net.lucypoulton.pronouns.common.platform.CommandSender;

import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class MigrateCommand implements ProNounsCommand {

public MigrateCommand(ProNouns plugin) {
this.plugin = plugin;
}

public enum MigrationSource {
YML,
MYSQL
}

private final ProNouns plugin;

private void execute(final CommandContext<CommandSender> context) {
final var logger = plugin.platform().logger();

if (!plugin.platform().migratable()) {
logger.warning(ProNounsTranslations.translate("pronouns.migrate.invalid-platform"));
return;
}
final var source = context.<MigrationSource>get("source");
logger.info(ProNounsTranslations.translate("pronouns.migrate.start"));
Map<UUID, List<PronounSet>> sets;

if (source == MigrationSource.YML) {
final var path = plugin.platform().dataDir().resolve("datastore.yml");
if (!Files.exists(path)) {
logger.warning("No legacy datastore found to migrate.");
return;
}
sets = LegacyMigrator.fromYaml(path);
} else {
throw new IllegalStateException("MySQL migration not yet supported");
}
plugin.store().setAll(sets);
logger.info(ProNounsTranslations.translate("pronouns.migrate.finish", sets.size()));
}

@Override
public Command.Builder<CommandSender> build(final Command.Builder<CommandSender> builder) {
return builder.literal("migrate")
.meta(CommandMeta.DESCRIPTION, CommandUtils.description("migrate"))
.permission(s -> s.uuid().isEmpty())
.argument(EnumArgument.builder(MigrationSource.class, "source"))
.flag(CommandFlag.builder("confirm"))
.handler(this::execute);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,11 @@ public interface Platform {
* Broadcasts a message to the console, and every player with a permission.
*/
void broadcast(Component component, String permission);

/**
* Returns whether this platform is capable of runnning ProNouns 2.x, and therefore may have data available to migrate.
*/
default boolean migratable() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,25 @@ public enum ProNounsPermission {

SET("pronouns.set", "Access to /pn set and /pn clear", 0),
SET_OTHER("pronouns.set.other",
"Access to /pn set and /pn clear with the --player flag to set others' pronouns.",
3),
"Access to /pn set and /pn clear with the --player flag to set others' pronouns.",
3),

GET("pronouns.get", "Access to /pn get", 0),

UPDATE("pronouns.update",
"Access to /pn update",
4),
"Access to /pn update",
4),
DEBUG("pronouns.debug",
"Access to /pn debug",
4),
"Access to /pn debug",
4),

RELOAD("pronouns.reload",
"Access to /pn reload",
4),
"Access to /pn reload",
4),

DUMP("pronouns.dump",
"Access to /pn dump",
4);
"Access to /pn dump",
4);

/**
* This permission's key.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package net.lucypoulton.pronouns.common.store;

import net.lucypoulton.pronouns.api.PronounStore;
import net.lucypoulton.pronouns.api.set.PronounSet;
import net.lucypoulton.pronouns.api.supplier.PronounSupplier;
import net.lucypoulton.pronouns.api.PronounParser;
import net.lucypoulton.pronouns.common.ProNouns;
import net.lucypoulton.pronouns.common.util.PropertiesUtil;
import org.jetbrains.annotations.NotNull;

Expand All @@ -12,15 +14,15 @@
import java.util.*;
import java.util.stream.Collectors;

public class FilePronounStore implements CachedPronounStore {

private boolean isDirty = false;
public class FilePronounStore implements PronounStore {
private final ProNouns plugin;
private final Path filePath;
private final Map<UUID, List<PronounSet>> sets = new HashMap<>();

private static final PronounParser parser = new PronounParser(PronounSet.builtins);

public FilePronounStore(final Path filePath) {
public FilePronounStore(final ProNouns plugin, final Path filePath) {
this.plugin = plugin;
this.filePath = filePath.resolve("pronouns-store.properties");
if (!Files.exists(this.filePath)) {
save();
Expand All @@ -47,28 +49,14 @@ public static void writeToFile(Map<UUID, List<PronounSet>> sets, Path path, Stri
}
}

private synchronized void save() {
if (!isDirty) return;
private void save() {
try {
writeToFile(sets, filePath, "ProNouns storage file. This file should not be edited while the server is running");
this.isDirty = false;
} catch (IOException e) {
throw new RuntimeException(e);
}
}

/**
* Stub - not needed
*/
@Override
public void onPlayerJoin(UUID uuid) {
}

@Override
public void onPlayerLeave(UUID uuid) {
this.save();
}

@Override
public PronounSupplier predefined() {
return PronounSet.builtins;
Expand All @@ -83,8 +71,13 @@ public List<PronounSet> sets(UUID player) {
public void set(UUID player, @NotNull List<PronounSet> sets) {
if (sets.size() == 0) this.sets.remove(player);
else this.sets.put(player, sets);
this.isDirty = true;
save();
plugin.executorService().execute(this::save);
}

@Override
public void setAll(Map<UUID, List<PronounSet>> sets) {
sets.forEach(this.sets::putIfAbsent);
plugin.executorService().execute(this::save);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public void set(UUID player, @NotNull List<PronounSet> sets) {
else storage.put(player, sets);
}

@Override
public void setAll(Map<UUID, List<PronounSet>> sets) {
sets.forEach(storage::putIfAbsent);
}

@Override
public Map<UUID, List<PronounSet>> dump() {
return Collections.unmodifiableMap(storage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public MySqlPronounStore(final ProNouns plugin, final Config.MySqlConnectionInfo
dataSource.addDataSourceProperty("prepStmtCacheSize", "250");
dataSource.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
dataSource.addDataSourceProperty("useServerPrepStmts ", "true");
dataSource.addDataSourceProperty("rewriteBatchedStatements", "true");

try (final var con = dataSource.getConnection()) {
con.prepareStatement("""
Expand Down Expand Up @@ -80,6 +81,21 @@ private void push(UUID uuid, List<PronounSet> sets) {
}
}

private void pushAll(Map<UUID, List<PronounSet>> sets) {
try (final var con = dataSource.getConnection()) {
final var stmt = con.prepareStatement("REPLACE INTO pronouns (player, pronouns, last_updated_from) VALUES (?, ?, ?)");
for (final var entry : sets.entrySet()) {
stmt.setString(1, entry.getKey().toString());
stmt.setString(2, parser.toString(entry.getValue()));
stmt.setString(3, plugin.meta().identifier());
stmt.addBatch();
}
stmt.executeBatch();
} catch (SQLException e) {
plugin.platform().logger().severe("Failed to write pronouns to MySQL: " + e.getMessage());
}
}

/**
* Polls the database to keep the cache up to date.
* Selects all entries where:
Expand Down Expand Up @@ -132,8 +148,15 @@ public void set(UUID player, @NotNull List<PronounSet> sets) {
plugin.executorService().submit(() -> push(player, sets));
}

@Override
public void setAll(Map<UUID, List<PronounSet>> sets) {
sets.forEach(cache::putIfAbsent);
plugin.executorService().submit(() -> pushAll(sets));
}

@Override
public Map<UUID, List<PronounSet>> dump() {
// fixme
throw new RuntimeException("L + ratio + get better");
}

Expand Down
Loading

0 comments on commit 8d2445a

Please sign in to comment.