Implemented saving to disk storage
This commit is contained in:
parent
32553b63ff
commit
b95d2f3953
@ -42,9 +42,9 @@ public class TestConfig implements WebMvcConfigurer {
|
||||
|
||||
@Bean
|
||||
void createNodes() {
|
||||
createNodeWithContent(new Node("game-servers", PickingMode.ROUND_ROBIN));
|
||||
createNodeWithContent(new Node("game-lobbies", PickingMode.LEAST_PICKED));
|
||||
createNodeWithContent(new Node("hub", PickingMode.SEQUENTIAL_FILLING));
|
||||
createNodeWithContent(new Node("game-servers", PickingMode.ROUND_ROBIN, false));
|
||||
createNodeWithContent(new Node("game-lobbies", PickingMode.LEAST_PICKED, false));
|
||||
createNodeWithContent(new Node("hub", PickingMode.SEQUENTIAL_FILLING, false));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@ -54,7 +54,7 @@ public class TestConfig implements WebMvcConfigurer {
|
||||
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
var slots = 5 * i;
|
||||
var room = Room.create("test-" + i, node, SlotLimit.of(slots), json.writeValueAsString(generatePayload()));
|
||||
var room = Room.create("test-" + i, node, SlotLimit.of(slots), json.writeValueAsString(generatePayload()), false);
|
||||
roomRepository.create(room);
|
||||
|
||||
for (int j = 0, n = rand.nextInt(slots + 1); j < n; j++) {
|
||||
@ -64,7 +64,7 @@ public class TestConfig implements WebMvcConfigurer {
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
var room = Room.create(randomUUID().toString(), node, SlotLimit.unlimited(), json.writeValueAsString(generatePayload()));
|
||||
var room = Room.create(randomUUID().toString(), node, SlotLimit.unlimited(), json.writeValueAsString(generatePayload()), false);
|
||||
room.setLocked((i & 1) == 0);
|
||||
roomRepository.create(room);
|
||||
}
|
||||
|
||||
@ -41,10 +41,10 @@ public class NodeController {
|
||||
@PostMapping
|
||||
ResponseEntity<?> registerNode(
|
||||
@Parameter(description = "Node identifier") @RequestParam(name = "nodeId") String nodeId,
|
||||
@Parameter(description = "Picking mode method") @RequestParam(name = "method") PickingMode method
|
||||
@Parameter(description = "Picking mode method") @RequestParam(name = "method") PickingMode method,
|
||||
@Parameter(description = "Save node") @RequestParam(name = "persist", required = false, defaultValue = "false") boolean persist
|
||||
) {
|
||||
|
||||
nodeService.create(new Node(nodeId, method));
|
||||
nodeService.create(new Node(nodeId, method, persist));
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
|
||||
@ -50,10 +50,11 @@ public class RoomController {
|
||||
@Parameter(description = "Room identifier") @RequestParam(name = "roomId") String roomId,
|
||||
@Parameter(description = "Maximum users count in room") @RequestParam(name = "slots") int slots,
|
||||
@Parameter(description = "Payload. Some data") @RequestParam(name = "payload") String payload,
|
||||
@Parameter(description = "Lock for picking") @RequestParam(name = "locked", defaultValue = "false") boolean locked
|
||||
@Parameter(description = "Lock for picking") @RequestParam(name = "locked", required = false, defaultValue = "false") boolean locked,
|
||||
@Parameter(description = "Save room") @RequestParam(name = "persist", required = false, defaultValue = "false") boolean persist
|
||||
) {
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
var room = Room.create(roomId, node, SlotLimit.of(slots), payload);
|
||||
var room = Room.create(roomId, node, SlotLimit.of(slots), payload, persist);
|
||||
room.setLocked(locked);
|
||||
roomService.create(room);
|
||||
|
||||
@ -105,6 +106,7 @@ public class RoomController {
|
||||
var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId));
|
||||
var room = roomService.find(node, roomId).orElseThrow(() -> new RoomNotFoundException(nodeId, roomId));
|
||||
room.setLocked(value);
|
||||
roomService.updateState(room);
|
||||
return ResponseEntity.ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import lombok.NonNull;
|
||||
import ru.dragonestia.picker.api.repository.response.type.RNode;
|
||||
import ru.dragonestia.picker.api.repository.response.type.type.PickingMode;
|
||||
|
||||
public record Node(@NonNull String id, @NonNull PickingMode mode) {
|
||||
public record Node(@NonNull String id, @NonNull PickingMode mode, boolean persist) {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
@ -16,10 +16,11 @@ public class Room {
|
||||
private final String nodeId;
|
||||
private final SlotLimit slots;
|
||||
private final String payload;
|
||||
private final boolean persist;
|
||||
private boolean locked = false;
|
||||
|
||||
public static Room create(String roomId, Node node, SlotLimit limit, String payload) {
|
||||
return new Room(roomId, node.id(), limit, payload);
|
||||
public static Room create(String roomId, Node node, SlotLimit limit, String payload, boolean persist) {
|
||||
return new Room(roomId, node.id(), limit, payload, persist);
|
||||
}
|
||||
|
||||
public void setLocked(boolean value) {
|
||||
|
||||
@ -9,7 +9,9 @@ public class SlotLimit {
|
||||
|
||||
public final static int UNLIMITED_VALUE = -1;
|
||||
|
||||
private final int slots;
|
||||
private int slots;
|
||||
|
||||
private SlotLimit() {}
|
||||
|
||||
private SlotLimit(int slots) {
|
||||
this.slots = slots;
|
||||
|
||||
@ -2,6 +2,7 @@ package ru.dragonestia.picker.repository;
|
||||
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -10,7 +11,7 @@ public interface NodeRepository {
|
||||
|
||||
void create(Node node) throws NodeAlreadyExistException;
|
||||
|
||||
void delete(Node node);
|
||||
List<Room> delete(Node node);
|
||||
|
||||
Optional<Node> find(String nodeId);
|
||||
|
||||
|
||||
@ -29,5 +29,5 @@ public interface RoomRepository {
|
||||
|
||||
void onCreateNode(Node node);
|
||||
|
||||
void onRemoveNode(Node node);
|
||||
List<Room> onRemoveNode(Node node);
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.dragonestia.picker.api.exception.NodeAlreadyExistException;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.repository.impl.cache.NodeId2PickerModeCache;
|
||||
@ -38,14 +39,14 @@ public class NodeRepositoryImpl implements NodeRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Node node) {
|
||||
public List<Room> delete(Node node) {
|
||||
synchronized (nodeMap) {
|
||||
nodeMap.remove(node.id());
|
||||
pickerRepository.remove(node.id());
|
||||
nodeId2PickerModeCache.remove(node.id());
|
||||
}
|
||||
|
||||
roomRepository.onRemoveNode(node);
|
||||
return roomRepository.onRemoveNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -122,10 +122,13 @@ public class RoomRepositoryImpl implements RoomRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoveNode(Node node) {
|
||||
public List<Room> onRemoveNode(Node node) {
|
||||
List<Room> deleted;
|
||||
synchronized (node2roomsMap) {
|
||||
deleted = node2roomsMap.get(node).values().stream().map(container -> container.room).toList();
|
||||
node2roomsMap.remove(node);
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
private record RoomContainer(Room room, AtomicInteger used) {
|
||||
|
||||
@ -24,11 +24,7 @@ public interface RoomService {
|
||||
|
||||
List<RRoom.Short> getAllRoomsWithDetailsResponse(Node node, Set<RoomDetails> details);
|
||||
|
||||
default int countAvailable(Node node) {
|
||||
return countAvailable(node, 1);
|
||||
}
|
||||
|
||||
int countAvailable(Node node, int requiredSlots);
|
||||
|
||||
Room pickAvailable(Node node, List<User> users);
|
||||
|
||||
void updateState(Room room);
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import ru.dragonestia.picker.api.repository.response.type.RNode;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.service.NodeService;
|
||||
import ru.dragonestia.picker.storage.NodeAndRoomStorage;
|
||||
import ru.dragonestia.picker.util.DetailsExtractor;
|
||||
import ru.dragonestia.picker.util.NamingValidator;
|
||||
|
||||
@ -24,16 +25,21 @@ public class NodeServiceImpl implements NodeService {
|
||||
private final NodeRepository nodeRepository;
|
||||
private final DetailsExtractor detailsExtractor;
|
||||
private final NamingValidator namingValidator;
|
||||
private final NodeAndRoomStorage storage;
|
||||
|
||||
@Override
|
||||
public void create(Node node) throws InvalidNodeIdentifierException, NodeAlreadyExistException {
|
||||
namingValidator.validateNodeId(node.id());
|
||||
nodeRepository.create(node);
|
||||
storage.saveNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Node node) {
|
||||
nodeRepository.delete(node);
|
||||
for (var room: nodeRepository.delete(node)) {
|
||||
storage.removeRoom(room);
|
||||
}
|
||||
storage.removeNode(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -12,6 +12,7 @@ import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.User;
|
||||
import ru.dragonestia.picker.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.service.RoomService;
|
||||
import ru.dragonestia.picker.storage.NodeAndRoomStorage;
|
||||
import ru.dragonestia.picker.util.DetailsExtractor;
|
||||
import ru.dragonestia.picker.util.NamingValidator;
|
||||
|
||||
@ -28,16 +29,19 @@ public class RoomServiceImpl implements RoomService {
|
||||
private final RoomRepository roomRepository;
|
||||
private final DetailsExtractor detailsExtractor;
|
||||
private final NamingValidator namingValidator;
|
||||
private final NodeAndRoomStorage storage;
|
||||
|
||||
@Override
|
||||
public void create(Room room) throws InvalidRoomIdentifierException, RoomAlreadyExistException {
|
||||
namingValidator.validateRoomId(room.getNodeId(), room.getId());
|
||||
roomRepository.create(room);
|
||||
storage.saveRoom(room);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(Room room) {
|
||||
roomRepository.remove(room);
|
||||
storage.removeRoom(room);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -59,14 +63,14 @@ public class RoomServiceImpl implements RoomService {
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countAvailable(Node node, int requiredSlots) {
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Room pickAvailable(Node node, List<User> users) {
|
||||
return roomRepository.pickFree(node, users)
|
||||
.orElseThrow(() -> new RuntimeException("There are no rooms available"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Room room) {
|
||||
storage.saveRoom(room);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package ru.dragonestia.picker.storage;
|
||||
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
|
||||
public interface NodeAndRoomStorage {
|
||||
|
||||
void loadAll();
|
||||
|
||||
void saveNode(Node node);
|
||||
|
||||
void removeNode(Node node);
|
||||
|
||||
void saveRoom(Room room);
|
||||
|
||||
void removeRoom(Room room);
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
package ru.dragonestia.picker.storage.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.repository.NodeRepository;
|
||||
import ru.dragonestia.picker.repository.RoomRepository;
|
||||
import ru.dragonestia.picker.storage.NodeAndRoomStorage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
|
||||
@Log4j2
|
||||
@Profile("!test")
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class FileStorageImpl implements NodeAndRoomStorage {
|
||||
|
||||
@Value("${ROOMPICKER_DATA_PATH:./appdata}")
|
||||
private String path;
|
||||
|
||||
private final NodeRepository nodeRepository;
|
||||
private final RoomRepository roomRepository;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void loadAll() {
|
||||
var dir = new File(path);
|
||||
var reader = objectMapper.reader();
|
||||
|
||||
if (!dir.exists()) dir.mkdirs();
|
||||
|
||||
var nodeDir = new File(path + "/nodes");
|
||||
if (!nodeDir.exists()) nodeDir.mkdir();
|
||||
for (var file: Objects.requireNonNull(nodeDir.listFiles(File::isFile))) {
|
||||
try {
|
||||
var node = reader.readValue(file, Node.class);
|
||||
nodeRepository.create(node);
|
||||
} catch (Exception ex) {
|
||||
log.error("Cannot read node file '%s'".formatted(file.getName()));
|
||||
log.throwing(ex);
|
||||
}
|
||||
}
|
||||
|
||||
var roomDir = new File(path + "/rooms");
|
||||
if (!roomDir.exists()) roomDir.mkdir();
|
||||
for (var file: Objects.requireNonNull(roomDir.listFiles(File::isFile))) {
|
||||
try {
|
||||
var room = reader.readValue(file, Room.class);
|
||||
roomRepository.create(room);
|
||||
} catch (Exception ex) {
|
||||
log.error("Cannot read room file '%s'".formatted(file.getName()));
|
||||
log.throwing(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveNode(Node node) {
|
||||
if (!node.persist()) return;
|
||||
var nodeFile = new File(path + "/nodes/" + node.id() + ".json");
|
||||
var writer = objectMapper.writer();
|
||||
|
||||
try {
|
||||
writer.writeValue(nodeFile, node);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNode(Node node) {
|
||||
if (!node.persist()) return;
|
||||
new File(path + "/nodes/" + node.id() + ".json").delete();
|
||||
|
||||
log.info("Removed node '%s' from disk storage".formatted(node.id()));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public void saveRoom(Room room) {
|
||||
if (!room.isPersist()) return;
|
||||
var roomFile = new File(path + "/rooms/" + room.getNodeId() + "." + room.getId() + ".json");
|
||||
var writer = objectMapper.writer();
|
||||
|
||||
try {
|
||||
writer.writeValue(roomFile, room);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRoom(Room room) {
|
||||
if (!room.isPersist()) return;
|
||||
new File(path + "/rooms/" + room.getNodeId() + "." + room.getId() + ".json").delete();
|
||||
|
||||
log.info("Removed room '%s/%s' from disk storage".formatted(room.getNodeId(), room.getId()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package ru.dragonestia.picker.storage.impl;
|
||||
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.dragonestia.picker.model.Node;
|
||||
import ru.dragonestia.picker.model.Room;
|
||||
import ru.dragonestia.picker.storage.NodeAndRoomStorage;
|
||||
|
||||
@Profile("test")
|
||||
@Component
|
||||
public class NullStorageImpl implements NodeAndRoomStorage {
|
||||
|
||||
@Override
|
||||
public void loadAll() {}
|
||||
|
||||
@Override
|
||||
public void saveNode(Node node) {}
|
||||
|
||||
@Override
|
||||
public void removeNode(Node node) {}
|
||||
|
||||
@Override
|
||||
public void saveRoom(Room room) {}
|
||||
|
||||
@Override
|
||||
public void removeRoom(Room room) {}
|
||||
}
|
||||
@ -9,7 +9,7 @@ import java.net.URI;
|
||||
@Configuration
|
||||
public class ServerConfig {
|
||||
|
||||
@Value("${DLB_HOST_URL:http://localhost:8080/}")
|
||||
@Value("${ROOMPICKER_HOST_URL:http://localhost:8080/}")
|
||||
private String serverUrl;
|
||||
|
||||
@Bean
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user