Switch to a batched solution for server heartbeats
This commit is contained in:
parent
37af5a7bd5
commit
63a9a51c5f
|
@ -4,15 +4,13 @@ import com.google.common.collect.Collections2;
|
|||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.Entity;
|
||||
import org.mongodb.morphia.annotations.Id;
|
||||
import org.mongodb.morphia.annotations.Indexed;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
@Entity(value = "grants", noClassnameStored = true)
|
||||
@AllArgsConstructor
|
||||
|
@ -36,6 +34,25 @@ public final class Grant {
|
|||
return APIv3.getDatastore().createQuery(Grant.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static Map<UUID, List<Grant>> byUserGrouped(Iterable<UUID> ids) {
|
||||
Map<UUID, List<Grant>> result = new HashMap<>();
|
||||
Set<UUID> uuidsToSearch = new HashSet<>();
|
||||
|
||||
for (UUID id : ids) {
|
||||
result.put(id, new ArrayList<>());
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
uuidsToSearch.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
APIv3.getDatastore().createQuery(Grant.class).field("user").in(uuidsToSearch).forEach((grant) -> {
|
||||
result.get(grant.getUser()).add(grant);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Grant() {} // For Morphia
|
||||
|
||||
public Grant(User user, String reason, Set<ServerGroup> scopes, Rank rank, Date expiresAt, User addedBy) {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package net.frozenorb.apiv3.models;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.actors.ActorType;
|
||||
import net.frozenorb.apiv3.utils.TimeUtils;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
import org.bson.types.ObjectId;
|
||||
import org.mongodb.morphia.annotations.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
@Entity(value = "punishments", noClassnameStored = true)
|
||||
@AllArgsConstructor
|
||||
|
@ -43,6 +43,29 @@ public final class Punishment {
|
|||
return APIv3.getDatastore().createQuery(Punishment.class).field("id").equal(id).get();
|
||||
}
|
||||
|
||||
public static Map<UUID, List<Punishment>> byUserGrouped(Iterable<UUID> ids) {
|
||||
return byUserGrouped(ids, ImmutableSet.copyOf(PunishmentType.values()));
|
||||
}
|
||||
|
||||
public static Map<UUID, List<Punishment>> byUserGrouped(Iterable<UUID> ids, Iterable<Punishment.PunishmentType> types) {
|
||||
Map<UUID, List<Punishment>> result = new HashMap<>();
|
||||
Set<UUID> uuidsToSearch = new HashSet<>();
|
||||
|
||||
for (UUID id : ids) {
|
||||
result.put(id, new ArrayList<>());
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
uuidsToSearch.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
APIv3.getDatastore().createQuery(Punishment.class).field("user").in(uuidsToSearch).field("type").in(types).forEach((punishment) -> {
|
||||
result.get(punishment.getUser()).add(punishment);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Punishment() {} // For Morphia
|
||||
|
||||
public Punishment(User user, String reason, PunishmentType type, Date expiresAt, User addedBy, Actor actor, Map<String, Object> metadata) {
|
||||
|
|
|
@ -53,6 +53,25 @@ public final class User {
|
|||
}
|
||||
}
|
||||
|
||||
public static Map<UUID, User> byIdGrouped(Iterable<UUID> ids) {
|
||||
Map<UUID, User> result = new HashMap<>();
|
||||
Set<UUID> uuidsToSearch = new HashSet<>();
|
||||
|
||||
for (UUID id : ids) {
|
||||
result.put(id, null);
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(id)) {
|
||||
uuidsToSearch.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
APIv3.getDatastore().createQuery(User.class).field("id").in(uuidsToSearch).forEach((user) -> {
|
||||
result.put(user.getId(), user);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static User byEmailToken(String name) {
|
||||
return APIv3.getDatastore().createQuery(User.class).field("emailToken").equal(name).get();
|
||||
}
|
||||
|
@ -128,7 +147,7 @@ public final class User {
|
|||
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).asList();
|
||||
}
|
||||
|
||||
public List<Punishment> getPunishments(Collection<Punishment.PunishmentType> types) {
|
||||
public List<Punishment> getPunishments(Iterable<Punishment.PunishmentType> types) {
|
||||
return APIv3.getDatastore().createQuery(Punishment.class).field("user").equal(id).field("type").in(types).asList();
|
||||
}
|
||||
|
||||
|
@ -204,9 +223,14 @@ public final class User {
|
|||
}
|
||||
|
||||
public Rank getHighestRank(ServerGroup serverGroup) {
|
||||
return getHighestRank(serverGroup, getGrants());
|
||||
}
|
||||
|
||||
// This is only used to help batch requests to mongo
|
||||
public Rank getHighestRank(ServerGroup serverGroup, Iterable<Grant> grants) {
|
||||
Rank highest = null;
|
||||
|
||||
for (Grant grant : getGrants()) {
|
||||
for (Grant grant : grants) {
|
||||
if (!grant.isActive() || (serverGroup != null && !grant.appliesOn(serverGroup))) {
|
||||
continue;
|
||||
}
|
||||
|
@ -252,14 +276,23 @@ public final class User {
|
|||
}
|
||||
|
||||
public Map<String, Object> getLoginInfo(Server server) {
|
||||
return getLoginInfo(
|
||||
server,
|
||||
getPunishments(ImmutableSet.of(
|
||||
Punishment.PunishmentType.BLACKLIST,
|
||||
Punishment.PunishmentType.BAN,
|
||||
Punishment.PunishmentType.MUTE
|
||||
)),
|
||||
getGrants()
|
||||
);
|
||||
}
|
||||
|
||||
// This is only used to help batch requests to mongo
|
||||
public Map<String, Object> getLoginInfo(Server server, Iterable<Punishment> punishments, Iterable<Grant> grants) {
|
||||
Punishment activeMute = null;
|
||||
String accessDenialReason = null;
|
||||
|
||||
for (Punishment punishment : getPunishments(ImmutableSet.of(
|
||||
Punishment.PunishmentType.BLACKLIST,
|
||||
Punishment.PunishmentType.BAN,
|
||||
Punishment.PunishmentType.MUTE
|
||||
))) {
|
||||
for (Punishment punishment : punishments) {
|
||||
if (!punishment.isActive()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -271,8 +304,7 @@ public final class User {
|
|||
}
|
||||
}
|
||||
|
||||
ServerGroup actorGroup = ServerGroup.byId(server.getServerGroup());
|
||||
Rank highestRank = getHighestRank(actorGroup);
|
||||
Rank highestRank = getHighestRank(ServerGroup.byId(server.getServerGroup()), grants);
|
||||
|
||||
// Generics are weird, yes we have to do this.
|
||||
ImmutableMap.Builder<String, Object> result = ImmutableMap.<String, Object>builder()
|
||||
|
|
|
@ -5,10 +5,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import net.frozenorb.apiv3.APIv3;
|
||||
import net.frozenorb.apiv3.actors.Actor;
|
||||
import net.frozenorb.apiv3.actors.ActorType;
|
||||
import net.frozenorb.apiv3.models.Rank;
|
||||
import net.frozenorb.apiv3.models.Server;
|
||||
import net.frozenorb.apiv3.models.ServerGroup;
|
||||
import net.frozenorb.apiv3.models.User;
|
||||
import net.frozenorb.apiv3.models.*;
|
||||
import net.frozenorb.apiv3.utils.ErrorUtils;
|
||||
import net.frozenorb.apiv3.utils.PermissionUtils;
|
||||
import net.frozenorb.apiv3.utils.UUIDUtils;
|
||||
|
@ -33,33 +30,51 @@ public final class POSTServerHeartbeat implements Route {
|
|||
Server actorServer = Server.byId(actor.getName());
|
||||
ServerGroup actorServerGroup = ServerGroup.byId(actorServer.getServerGroup());
|
||||
Document reqJson = Document.parse(req.body());
|
||||
Set<UUID> onlinePlayers = new HashSet<>();
|
||||
Map<UUID, String> onlinePlayersNames = new HashMap<>();
|
||||
Map<UUID, User> onlinePlayersUsers = new HashMap<>();
|
||||
Map<UUID, List<Grant>> onlinePlayersGrants = new HashMap<>();
|
||||
Map<UUID, List<Punishment>> onlinePlayersPunishments = new HashMap<>();
|
||||
Map<String, Object> playersResponse = new HashMap<>();
|
||||
|
||||
// This code is messy, but we have to do db ops in parallel to avoid
|
||||
// spamming Mongo with queries, so we do this.
|
||||
for (Object player : (List<Object>) reqJson.get("players")) {
|
||||
Document playerJson = (Document) player;
|
||||
UUID uuid = UUID.fromString(playerJson.getString("uuid"));
|
||||
|
||||
if (!UUIDUtils.isAcceptableUUID(uuid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
User user = User.byId(uuid);
|
||||
String username = playerJson.getString("username");
|
||||
|
||||
if (UUIDUtils.isAcceptableUUID(uuid)) {
|
||||
onlinePlayersNames.put(uuid, username);
|
||||
}
|
||||
}
|
||||
|
||||
onlinePlayersUsers = User.byIdGrouped(onlinePlayersNames.keySet());
|
||||
|
||||
for (Map.Entry<UUID, User> entry : new HashMap<>(onlinePlayersUsers).entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
User user = entry.getValue();
|
||||
|
||||
if (user == null) {
|
||||
// Will be saved by the save command a few lines down.
|
||||
// Will be saved in the User constructor
|
||||
String username = onlinePlayersNames.get(uuid);
|
||||
user = new User(uuid, username);
|
||||
APIv3.getDatastore().save(user);
|
||||
onlinePlayersUsers.put(uuid, user);
|
||||
}
|
||||
|
||||
// Only save if needed
|
||||
if (user.seenOnServer(actorServer)) {
|
||||
APIv3.getDatastore().save(user);
|
||||
}
|
||||
}
|
||||
|
||||
onlinePlayers.add(user.getId());
|
||||
playersResponse.put(user.getId().toString(), user.getLoginInfo(actorServer));
|
||||
onlinePlayersGrants = Grant.byUserGrouped(onlinePlayersUsers.keySet());
|
||||
onlinePlayersPunishments = Punishment.byUserGrouped(onlinePlayersUsers.keySet());
|
||||
|
||||
for (Map.Entry<UUID, User> entry : onlinePlayersUsers.entrySet()) {
|
||||
UUID uuid = entry.getKey();
|
||||
User user = entry.getValue();
|
||||
|
||||
playersResponse.put(uuid.toString(), user.getLoginInfo(actorServer, onlinePlayersPunishments.get(uuid), onlinePlayersGrants.get(uuid)));
|
||||
}
|
||||
|
||||
for (Object event : (List<Object>) reqJson.get("events")) {
|
||||
|
@ -78,7 +93,7 @@ public final class POSTServerHeartbeat implements Route {
|
|||
}
|
||||
}
|
||||
|
||||
Map<String, Map<String, Boolean>> permissions = new HashMap<>();
|
||||
Map<String, Map<String, Boolean>> permissionsResponse = new HashMap<>();
|
||||
|
||||
for (Rank rank : Rank.values()) {
|
||||
Map<String, Boolean> scopedPermissions = PermissionUtils.mergePermissions(
|
||||
|
@ -86,17 +101,17 @@ public final class POSTServerHeartbeat implements Route {
|
|||
actorServerGroup.calculatePermissions(rank)
|
||||
);
|
||||
|
||||
permissions.put(rank.getId(), scopedPermissions);
|
||||
permissionsResponse.put(rank.getId(), scopedPermissions);
|
||||
}
|
||||
|
||||
actorServer.setPlayers(onlinePlayers);
|
||||
actorServer.setPlayers(onlinePlayersNames.keySet());
|
||||
actorServer.setLastTps(reqJson.getDouble("lastTps"));
|
||||
actorServer.setLastUpdatedAt(new Date());
|
||||
APIv3.getDatastore().save(actorServer);
|
||||
|
||||
return ImmutableMap.of(
|
||||
"players", playersResponse,
|
||||
"permissions", permissions
|
||||
"permissions", permissionsResponse
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue