From dce1d8adc3ea2e18d2f0b1dedf6c6fbc8dee30f1 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 12:10:18 +0700 Subject: [PATCH 01/13] Implemented picking method in Client and refactored picking in server --- .../impl/repository/NodeRepositoryImpl.java | 6 +++++- .../picker/api/impl/util/RestTemplate.java | 20 ++++++++++++++++--- .../picker/controller/NodeController.java | 2 +- .../picker/service/impl/RoomServiceImpl.java | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/NodeRepositoryImpl.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/NodeRepositoryImpl.java index 08034a3..2c195b3 100644 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/NodeRepositoryImpl.java +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/repository/NodeRepositoryImpl.java @@ -76,6 +76,10 @@ public void saveNode(@NotNull NodeDefinition definition) { @Override public @NotNull PickedRoomResponse pickRoom(@NotNull NodeIdentifier identifier, @NotNull Set users) { - throw new UnsupportedOperationException("Not implemented"); + return rest.queryPostWithBody( + "/nodes/" + identifier.getValue() + "/pick", + PickedRoomResponse.class, + params -> {}, String.join(",", users.stream().map(user -> user.getValue()).toList()) + ); } } diff --git a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java index 4d1dbbe..9e440be 100644 --- a/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java +++ b/client-impl/src/main/java/ru/dragonestia/picker/api/impl/util/RestTemplate.java @@ -3,9 +3,7 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import okhttp3.FormBody; -import okhttp3.OkHttpClient; -import okhttp3.Response; +import okhttp3.*; import org.jetbrains.annotations.ApiStatus.Internal; import ru.dragonestia.picker.api.exception.ExceptionFactory; import ru.dragonestia.picker.api.impl.RoomPickerClient; @@ -78,6 +76,22 @@ public T query(String uri, HttpMethod method, Class clazz, ParamsConsumer } } + public T queryPostWithBody(String uri, Class clazz, ParamsConsumer paramsConsumer, String body) { + var request = client.prepareRequestBuilder(uri + queryEncode(paramsConsumer)) + .post(RequestBody.create(body, MediaType.get("text/plain"))) + .build(); + + try (var response = httpClient.newCall(request).execute()) { + checkResponseForErrors(response); + + return json.readValue(new String(Objects.requireNonNull(response.body()).bytes(), StandardCharsets.UTF_8), clazz); + } catch (JsonProcessingException ex) { + throw new RuntimeException("Json processing error", ex); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + private String queryEncode(ParamsConsumer paramsConsumer) { var params = new HashMap(); paramsConsumer.accept(params); diff --git a/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java b/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java index c72a527..8bdd6b7 100644 --- a/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java +++ b/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java @@ -77,7 +77,7 @@ ResponseEntity removeNode( @PostMapping("/{nodeId}/pick") ResponseEntity pickRoom( @Parameter(description = "Node identifier") @PathVariable("nodeId") String nodeId, - @Parameter(description = "Users to add", example = "user1,user3,user3") @RequestParam(name = "userIds") String userIds + @RequestBody String userIds ) { namingValidator.validateNodeId(nodeId); diff --git a/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java b/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java index af05a0f..eccfe3d 100644 --- a/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java @@ -80,7 +80,7 @@ public List getAllRoomsWithDetailsResponse(Node node, Set users) { var room = roomRepository.pickFree(node, users) - .orElseThrow(() -> new RuntimeException("There are no rooms available")); + .orElseThrow(() -> new RuntimeException("There are no rooms available. Given users count: " + users.size())); var roomUsers = userRepository.usersOf(room); return new PickedRoomResponse( From 96117057053c089ccc0189cdbfe321a434b7283b Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 12:10:51 +0700 Subject: [PATCH 02/13] Fixed NullPointerException in UserMetricsAspect --- .../java/ru/dragonestia/picker/aspect/UserMetricsAspect.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java b/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java index a268434..c4c240e 100644 --- a/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java +++ b/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java @@ -20,6 +20,7 @@ import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -126,7 +127,9 @@ void onRoomChangeLockState(UpdateRoomLockStateEvent event) { @Scheduled(fixedDelay = 3_000) void updateUserMetrics() { - userRepository.countUsersForNodes().forEach((nodeId, users) -> data.get(nodeId).users().set(users)); + userRepository.countUsersForNodes().forEach((nodeId, users) -> { + Optional.ofNullable(data.get(nodeId)).ifPresent(node -> node.users().set(users)); + }); } private record NodeData(Gauge usersGauge, AtomicInteger users, Counter picksPerMinute, Set locked, Gauge lockedGauge, Gauge roomsGauge) {} From a81676fa9a19cd281cc06dcc59eeb7874a0cf87d Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 12:12:27 +0700 Subject: [PATCH 03/13] Implemented module 'noiser' - stress testing --- noiser/build.gradle | 22 +++ .../ru/dragonestia/picker/noiser/Main.java | 127 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 noiser/build.gradle create mode 100644 noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java diff --git a/noiser/build.gradle b/noiser/build.gradle new file mode 100644 index 0000000..0efd63f --- /dev/null +++ b/noiser/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'java' +} + +group = 'ru.dragonestia' +version = 'unspecified' + +repositories { + mavenCentral() +} + +dependencies { + implementation project(":client-api") + implementation project(":client-impl") + + testImplementation platform('org.junit:junit-bom:5.9.1') + testImplementation 'org.junit.jupiter:junit-jupiter' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java b/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java new file mode 100644 index 0000000..256172f --- /dev/null +++ b/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java @@ -0,0 +1,127 @@ +package ru.dragonestia.picker.noiser; + +import ru.dragonestia.picker.api.impl.RoomPickerClient; +import ru.dragonestia.picker.api.model.node.NodeDefinition; +import ru.dragonestia.picker.api.model.node.PickingMethod; +import ru.dragonestia.picker.api.model.room.RoomDefinition; +import ru.dragonestia.picker.api.repository.query.node.GetAllNodes; +import ru.dragonestia.picker.api.repository.query.user.UnlinkUsersFromRoom; +import ru.dragonestia.picker.api.repository.type.NodeIdentifier; +import ru.dragonestia.picker.api.repository.type.RoomIdentifier; +import ru.dragonestia.picker.api.repository.type.UserIdentifier; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + + +public class Main { + + private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(8); + private final Random random = new Random(); + private final RoomPickerClient client; + private final List nodes; + + private final Map totalUsers = new ConcurrentHashMap<>(); + private final int expectedUsers = 10000; + + public Main() { + client = new RoomPickerClient("http://localhost:8080", "admin", "qwerty123"); + nodes = initNodes(); + } + + private void removeAll() { + client.getNodeRepository().allNodes(GetAllNodes.JUST) + .forEach(node -> client.getNodeRepository().removeNode(node)); + } + + private List initNodes() { + removeAll(); + + var list = new ArrayList(); + + for (int i = 0; i < 5; i++) { + var node = new NodeDefinition(NodeIdentifier.of("test-node-" + i)) + .setPickingMethod(PickingMethod.values()[i % PickingMethod.values().length]); + + client.getNodeRepository().saveNode(node); + + var nodeId = node.getIdentifierObject(); + totalUsers.put(nodeId, new AtomicInteger(0)); + list.add(nodeId); + } + + return list; + } + + private void initRooms() { + final int perNode = expectedUsers / nodes.size(); + final int roomsPerNode = perNode / 10; + + for (var nodeId: nodes) { + for (int i = 0; i < roomsPerNode; i++) { + client.getRoomRepository().saveRoom( + new RoomDefinition(nodeId, RoomIdentifier.of(UUID.randomUUID().toString())).setMaxSlots(roomsPerNode) + ); + } + } + } + + private void pickUsers() { + for (var nodeId: nodes) { + var usersInNode = totalUsers.get(nodeId); + + try { + synchronized (usersInNode) { + var users = new HashSet(); + var maxAdd = Math.min(10, (expectedUsers / nodes.size()) - usersInNode.get()); + + if (maxAdd == 0) return; + var add = maxAdd == 1 ? 1 : (random.nextInt(maxAdd - 1) + 1); + for (int i = 0; i < add; i++) { + users.add(UserIdentifier.of(UUID.randomUUID().toString())); + } + + var request = client.getNodeRepository().pickRoom(nodeId, users); + usersInNode.addAndGet(add); + var roomId = RoomIdentifier.of(request.roomId()); + + System.out.printf("Added %s(total %s) users to %s/%s%n", add, usersInNode.get(), nodeId.getValue(), roomId.getValue()); + + scheduler.schedule(() -> { + try { + client.getUserRepository().unlinkUsersFromRoom(UnlinkUsersFromRoom.builder() + .setNodeId(nodeId) + .setRoomId(roomId) + .setUsers(users) + .build()); + + usersInNode.addAndGet(-add); + System.out.printf("Reduced %s users from %s/%s%n", add, nodeId.getValue(), roomId.getValue()); + } catch (Exception ex) { + System.out.println("Error(" + ex.getClass().getSimpleName() + "): " + ex.getMessage()); + } + }, random.nextInt(1000) + 100, TimeUnit.MILLISECONDS); + } + } catch (Exception ex) { + System.out.println("Error(" + ex.getClass().getSimpleName() + "): " + ex.getMessage()); + } + } + } + + public void startNoise() throws InterruptedException { + initRooms(); + + while (true) { + pickUsers(); + Thread.sleep(10); + } + } + + public static void main(String[] args) throws InterruptedException { + new Main().startNoise(); + } +} \ No newline at end of file From a5d11ec9e9753658a1f5587b92078cf912b3b8c6 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 15:05:27 +0700 Subject: [PATCH 04/13] Refactored repositories, deleted shit code --- .../picker/aspect/UserMetricsAspect.java | 2 +- .../picker/controller/NodeController.java | 8 +- .../picker/controller/UserRoomController.java | 11 +- .../ru/dragonestia/picker/model/Node.java | 5 + .../picker/repository/NodeRepository.java | 5 +- .../picker/repository/RoomRepository.java | 11 +- .../picker/repository/UserRepository.java | 10 +- .../repository/impl/ContainerRepository.java | 47 ++++ .../repository/impl/NodeRepositoryImpl.java | 63 +----- .../repository/impl/PickerRepository.java | 58 ----- .../repository/impl/RoomRepositoryImpl.java | 160 +++----------- .../repository/impl/UserRepositoryImpl.java | 201 +++++------------- .../impl/cache/NodeId2PickerModeCache.java | 25 --- .../impl/container/NodeContainer.java | 104 +++++++++ .../impl/container/RoomContainer.java | 105 +++++++++ .../impl/picker/LeastPickedPicker.java | 16 +- .../repository/impl/picker/RoomPicker.java | 4 +- .../repository/impl/picker/RoomWrapper.java | 25 +-- .../impl/picker/RoundRobinPicker.java | 18 +- .../impl/picker/SequentialFillingPicker.java | 18 +- .../repository/impl/type/UserTransaction.java | 13 ++ .../picker/service/RoomService.java | 5 +- .../picker/service/UserService.java | 4 +- .../picker/service/impl/NodeServiceImpl.java | 9 +- .../picker/service/impl/RoomServiceImpl.java | 14 +- .../picker/service/impl/UserServiceImpl.java | 4 +- .../picker/util/NamingValidator.java | 7 +- .../picker/picker/LeastPickedTests.java | 4 +- .../picker/picker/RoundRobinTests.java | 5 +- .../picker/picker/SequentialFillingTests.java | 4 +- 30 files changed, 448 insertions(+), 517 deletions(-) create mode 100644 server/src/main/java/ru/dragonestia/picker/repository/impl/ContainerRepository.java delete mode 100644 server/src/main/java/ru/dragonestia/picker/repository/impl/PickerRepository.java delete mode 100644 server/src/main/java/ru/dragonestia/picker/repository/impl/cache/NodeId2PickerModeCache.java create mode 100644 server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java create mode 100644 server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java create mode 100644 server/src/main/java/ru/dragonestia/picker/repository/impl/type/UserTransaction.java diff --git a/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java b/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java index c4c240e..ee0003d 100644 --- a/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java +++ b/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java @@ -115,7 +115,7 @@ void onDeleteNode(Node node) { meterRegistry.remove(data.roomsGauge()); } - @AfterReturning(value = "execution(* ru.dragonestia.picker.repository.RoomRepository.pickFree(ru.dragonestia.picker.model.Node, *)) && args(node, ..)", argNames = "node") + @AfterReturning(value = "execution(* ru.dragonestia.picker.repository.RoomRepository.pick(ru.dragonestia.picker.model.Node, *)) && args(node, ..)", argNames = "node") void onPickRoom(Node node) { data.get(node.getIdentifier()).picksPerMinute().increment(); } diff --git a/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java b/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java index 8bdd6b7..d07e5d5 100644 --- a/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java +++ b/server/src/main/java/ru/dragonestia/picker/controller/NodeController.java @@ -8,17 +8,21 @@ import org.springframework.web.bind.annotation.*; import ru.dragonestia.picker.api.exception.NodeNotFoundException; import ru.dragonestia.picker.api.model.node.PickingMethod; +import ru.dragonestia.picker.api.model.user.UserDefinition; import ru.dragonestia.picker.api.repository.response.NodeDetailsResponse; import ru.dragonestia.picker.api.repository.response.NodeListResponse; import ru.dragonestia.picker.api.repository.response.PickedRoomResponse; import ru.dragonestia.picker.api.repository.type.NodeIdentifier; +import ru.dragonestia.picker.api.repository.type.UserIdentifier; import ru.dragonestia.picker.model.Node; +import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.service.NodeService; import ru.dragonestia.picker.service.RoomService; import ru.dragonestia.picker.util.DetailsParser; import ru.dragonestia.picker.util.NamingValidator; import java.util.Arrays; +import java.util.stream.Collectors; @Tag(name = "Nodes", description = "Node management") @RestController @@ -82,7 +86,9 @@ ResponseEntity pickRoom( namingValidator.validateNodeId(nodeId); var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId)); - var users = namingValidator.validateUserIds(Arrays.stream(userIds.split(",")).toList()); + var users = Arrays.stream(userIds.split(",")) + .map(userId -> new User(UserIdentifier.of(userId))) + .collect(Collectors.toSet()); var response = roomService.pickAvailable(node, users); return ResponseEntity.ok(response); diff --git a/server/src/main/java/ru/dragonestia/picker/controller/UserRoomController.java b/server/src/main/java/ru/dragonestia/picker/controller/UserRoomController.java index 6b5e9b0..180e0fe 100644 --- a/server/src/main/java/ru/dragonestia/picker/controller/UserRoomController.java +++ b/server/src/main/java/ru/dragonestia/picker/controller/UserRoomController.java @@ -10,8 +10,10 @@ import ru.dragonestia.picker.api.exception.RoomNotFoundException; import ru.dragonestia.picker.api.repository.response.LinkUsersWithRoomResponse; import ru.dragonestia.picker.api.repository.response.RoomUserListResponse; +import ru.dragonestia.picker.api.repository.type.UserIdentifier; import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Node; +import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.service.RoomService; import ru.dragonestia.picker.service.NodeService; import ru.dragonestia.picker.service.UserService; @@ -19,6 +21,7 @@ import ru.dragonestia.picker.util.NamingValidator; import java.util.Arrays; +import java.util.stream.Collectors; @Tag(name = "Users", description = "User management") @RequiredArgsConstructor @@ -54,7 +57,9 @@ ResponseEntity linkUserWithRoom( @Parameter(description = "Ignore slot limitation") @RequestParam(name = "force") boolean force ) { var room = getNodeAndRoom(nodeId, roomId).room(); - var users = namingValidator.validateUserIds(Arrays.stream(userIds.split(",")).toList()); + var users = Arrays.stream(userIds.split(",")) + .map(userId -> new User(UserIdentifier.of(userId))) + .collect(Collectors.toSet()); userService.linkUsersWithRoom(room, users, force); var usedSlots = userService.getRoomUsers(room).size(); return ResponseEntity.ok(new LinkUsersWithRoomResponse(usedSlots, room.getMaxSlots())); @@ -69,7 +74,9 @@ ResponseEntity unlinkUsersForBucket( ) { var room = getNodeAndRoom(nodeId, roomId).room(); - var users = namingValidator.validateUserIds(Arrays.stream(userIds.split(",")).toList()); + var users = Arrays.stream(userIds.split(",")) + .map(userId -> new User(UserIdentifier.of(userId))) + .collect(Collectors.toSet()); userService.unlinkUsersFromRoom(room, users); return ResponseEntity.ok().build(); } diff --git a/server/src/main/java/ru/dragonestia/picker/model/Node.java b/server/src/main/java/ru/dragonestia/picker/model/Node.java index 36e704c..ab49bdd 100644 --- a/server/src/main/java/ru/dragonestia/picker/model/Node.java +++ b/server/src/main/java/ru/dragonestia/picker/model/Node.java @@ -58,4 +58,9 @@ public boolean equals(Object object) { } return false; } + + @Override + public String toString() { + return "{Node id='%s'}".formatted(identifier); + } } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/NodeRepository.java b/server/src/main/java/ru/dragonestia/picker/repository/NodeRepository.java index 7aa60da..86b4183 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/NodeRepository.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/NodeRepository.java @@ -2,7 +2,6 @@ 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; @@ -11,9 +10,9 @@ public interface NodeRepository { void create(Node node) throws NodeAlreadyExistException; - List delete(Node node); + void delete(Node node); - Optional find(String nodeId); + Optional findById(String nodeId); List all(); } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/RoomRepository.java b/server/src/main/java/ru/dragonestia/picker/repository/RoomRepository.java index 6910fb1..0b17c3a 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/RoomRepository.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/RoomRepository.java @@ -1,13 +1,14 @@ package ru.dragonestia.picker.repository; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.api.exception.RoomAlreadyExistException; import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.User; import java.util.Collection; -import java.util.List; import java.util.Optional; +import java.util.Set; public interface RoomRepository { @@ -17,11 +18,7 @@ public interface RoomRepository { Optional find(Node node, String identifier); - List all(Node node); + Collection all(Node node); - Optional pickFree(Node node, Collection users); - - void onCreateNode(Node node); - - List onRemoveNode(Node node); + Room pick(Node node, Set users) throws NoRoomsAvailableException; } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/UserRepository.java b/server/src/main/java/ru/dragonestia/picker/repository/UserRepository.java index b8ccb9e..88d75d1 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/UserRepository.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/UserRepository.java @@ -10,17 +10,15 @@ public interface UserRepository { - int linkWithRoom(Room room, Collection users, boolean force) throws RoomAreFullException; + void linkWithRoom(Room room, Collection users, boolean force) throws RoomAreFullException; void unlinkWithRoom(Room room, Collection users); - List findAllLinkedUserRooms(User user); + Collection findAllLinkedUserRooms(User user); - void onRemoveRoom(Room room); + Collection usersOf(Room room); - List usersOf(Room room); - - List search(String input); + Collection search(String input); int countAllUsers(); diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/ContainerRepository.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/ContainerRepository.java new file mode 100644 index 0000000..5d9e02c --- /dev/null +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/ContainerRepository.java @@ -0,0 +1,47 @@ +package ru.dragonestia.picker.repository.impl; + +import lombok.extern.log4j.Log4j2; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; +import ru.dragonestia.picker.api.exception.NodeAlreadyExistException; +import ru.dragonestia.picker.model.Node; +import ru.dragonestia.picker.repository.impl.container.NodeContainer; +import ru.dragonestia.picker.repository.impl.type.UserTransaction; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class ContainerRepository { + + private final Map containers = new ConcurrentHashMap<>(); + + private UserTransaction.Listener transactionListener = transaction -> {}; + + public void create(Node node) throws NodeAlreadyExistException { + if (containers.containsKey(node.getIdentifier())) { + throw new NodeAlreadyExistException(node.getIdentifier()); + } + + var container = new NodeContainer(node, transactionListener); + containers.put(node.getIdentifier(), container); + } + + public void remove(@NotNull String nodeId) { + containers.remove(nodeId); + } + + public @NotNull Optional findById(@NotNull String nodeId) { + return Optional.ofNullable(containers.get(nodeId)); + } + + public @NotNull Collection all() { + return containers.values(); + } + + public void setTransactionListener(@NotNull UserTransaction.Listener transactionListener) { + this.transactionListener = transactionListener; + } +} diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/NodeRepositoryImpl.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/NodeRepositoryImpl.java index defb133..8503c01 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/NodeRepositoryImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/NodeRepositoryImpl.java @@ -1,80 +1,39 @@ package ru.dragonestia.picker.repository.impl; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; +import lombok.extern.log4j.Log4j2; +import org.springframework.stereotype.Component; 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; +import ru.dragonestia.picker.repository.impl.container.NodeContainer; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -@Repository +@Component @RequiredArgsConstructor public class NodeRepositoryImpl implements NodeRepository { - private final RoomRepository roomRepository; - private final PickerRepository pickerRepository; - private final NodeId2PickerModeCache nodeId2PickerModeCache; - private final Map nodeMap = new HashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final ContainerRepository containerRepository; @Override public void create(Node node) throws NodeAlreadyExistException { - lock.writeLock().lock(); - try { - if (nodeMap.containsKey(node.getIdentifier())) { - throw new NodeAlreadyExistException(node.getIdentifier()); - } - - nodeMap.put(node.getIdentifier(), node); - var picker = pickerRepository.create(node.getIdentifier(), node.getPickingMethod()); - nodeId2PickerModeCache.put(node.getIdentifier(), picker); - - roomRepository.onCreateNode(node); - } finally { - lock.writeLock().unlock(); - } + containerRepository.create(node); } @Override - public List delete(Node node) { - lock.writeLock().lock(); - try { - nodeMap.remove(node.getIdentifier()); - pickerRepository.remove(node.getIdentifier()); - nodeId2PickerModeCache.remove(node.getIdentifier()); - - return roomRepository.onRemoveNode(node); - } finally { - lock.writeLock().unlock(); - } + public void delete(Node node) { + containerRepository.remove(node.getIdentifier()); } @Override - public Optional find(String nodeId) { - lock.readLock().lock(); - try { - return nodeMap.containsKey(nodeId)? Optional.of(nodeMap.get(nodeId)) : Optional.empty(); - } finally { - lock.readLock().unlock(); - } + public Optional findById(String nodeId) { + return containerRepository.findById(nodeId).map(NodeContainer::getNode); } @Override public List all() { - lock.readLock().lock(); - try { - return nodeMap.values().stream().toList(); - } finally { - lock.readLock().unlock(); - } + return containerRepository.all().stream().map(NodeContainer::getNode).toList(); } } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/PickerRepository.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/PickerRepository.java deleted file mode 100644 index 536a0c3..0000000 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/PickerRepository.java +++ /dev/null @@ -1,58 +0,0 @@ -package ru.dragonestia.picker.repository.impl; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import ru.dragonestia.picker.api.model.node.PickingMethod; -import ru.dragonestia.picker.model.Room; -import ru.dragonestia.picker.model.User; -import ru.dragonestia.picker.repository.UserRepository; -import ru.dragonestia.picker.repository.impl.picker.*; - -import java.security.InvalidParameterException; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Component -@RequiredArgsConstructor -public class PickerRepository { - - private final UserRepository userRepository; - private final Map pickers = new ConcurrentHashMap<>(); - - public RoomPicker create(String nodeId, PickingMethod mode) { - var picker = of(mode); - pickers.put(nodeId, picker); - return picker; - } - - public void remove(String nodeId) { - pickers.remove(nodeId); - } - - public RoomPicker find(String nodeId) { - return pickers.get(nodeId); - } - - public Room pick(String nodeId, Collection users) { - return pickers.get(nodeId).pick(users); - } - - private RoomPicker of(PickingMethod mode) { - switch (mode) { - case SEQUENTIAL_FILLING -> { - return new SequentialFillingPicker(userRepository); - } - - case ROUND_ROBIN -> { - return new RoundRobinPicker(userRepository); - } - - case LEAST_PICKED -> { - return new LeastPickedPicker(userRepository); - } - - default -> throw new InvalidParameterException("Taken: " + mode); - } - } -} diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/RoomRepositoryImpl.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/RoomRepositoryImpl.java index 8d76f11..fe570a0 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/RoomRepositoryImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/RoomRepositoryImpl.java @@ -1,163 +1,61 @@ package ru.dragonestia.picker.repository.impl; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Component; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.api.exception.NodeNotFoundException; import ru.dragonestia.picker.api.exception.RoomAlreadyExistException; -import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.Node; +import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.repository.RoomRepository; -import ru.dragonestia.picker.repository.UserRepository; +import ru.dragonestia.picker.repository.impl.container.NodeContainer; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.Collection; +import java.util.Optional; +import java.util.Set; -@Repository +@Component @RequiredArgsConstructor public class RoomRepositoryImpl implements RoomRepository { - private final UserRepository userRepository; - private final PickerRepository pickerRepository; - private final Map node2roomsMap = new HashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(true); + private final ContainerRepository containerRepository; @Override public void create(Room room) throws RoomAlreadyExistException { - var nodeId = room.getNodeIdentifier(); - - lock.writeLock().lock(); - try { - var node = node2roomsMap.keySet().stream() - .filter(n -> room.getNodeIdentifier().equals(n.getIdentifier())) - .findFirst(); - - if (node.isEmpty()) { - throw new IllegalArgumentException("Node '" + nodeId + "' does not exist"); - } - - var rooms = node2roomsMap.get(node.get()); - if (rooms.containsKey(room.getIdentifier())) { - throw new RoomAlreadyExistException(room.getNodeIdentifier(), room.getIdentifier()); - } - rooms.put(room.getIdentifier(), new RoomContainer(room, new AtomicInteger(0))); - pickerRepository.find(room.getNodeIdentifier()).add(room); - } finally { - lock.writeLock().unlock(); - } + containerRepository.findById(room.getNodeIdentifier()) + .orElseThrow(() -> new NodeNotFoundException(room.getNodeIdentifier())) + .addRoom(room); } @Override public void remove(Room room) { - var nodeId = room.getNodeIdentifier(); - var node = node2roomsMap.keySet().stream() - .filter(n -> room.getNodeIdentifier().equals(n.getIdentifier())) - .findFirst(); - - lock.writeLock().lock(); - try { - if (node.isEmpty()) { - throw new NodeNotFoundException("Node '" + nodeId + "' does not exist"); - } - - node2roomsMap.get(node.get()).remove(room.getIdentifier()); - pickerRepository.find(room.getNodeIdentifier()).remove(room); - - userRepository.onRemoveRoom(room); - } finally { - lock.writeLock().unlock(); - } + containerRepository.findById(room.getNodeIdentifier()) + .orElseThrow(() -> new NodeNotFoundException(room.getNodeIdentifier())) + .removeRoom(room); } @Override public Optional find(Node node, String identifier) { - lock.readLock().lock(); - try { - if (!node2roomsMap.containsKey(node)) { - throw new NodeNotFoundException("Node '" + node.getIdentifier() + "' does not exist"); - } - - var result = node2roomsMap.get(node).getOrDefault(identifier, null); - return result == null? Optional.empty() : Optional.of(result.room()); - } finally { - lock.readLock().unlock(); - } + return containerRepository.findById(node.getIdentifier()) + .orElseThrow(() -> new NodeNotFoundException(node.getIdentifier())) + .findRoomById(identifier) + .map(RoomContainer::getRoom); } @Override - public List all(Node node) { - lock.readLock().lock(); - try { - if (!node2roomsMap.containsKey(node)) { - throw new NodeNotFoundException("Node '%s' does not exists".formatted(node.getIdentifier())); - } - - return node2roomsMap.get(node).values().stream().map(RoomContainer::room).toList(); - } finally { - lock.readLock().unlock(); - } + public Collection all(Node node) { + return containerRepository.findById(node.getIdentifier()) + .orElseThrow(() -> new NodeNotFoundException(node.getIdentifier())) + .allRooms() + .stream().map(RoomContainer::getRoom).toList(); } @Override - public Optional pickFree(Node node, Collection users) { - lock.writeLock().lock(); - try { - if (!node2roomsMap.containsKey(node)) { - throw new NodeNotFoundException("Node '" + node.getIdentifier() + "' does not exist"); - } - - Room room = null; - try { - room = pickerRepository.pick(node.getIdentifier(), users); - } catch (RuntimeException ignore) {} // TODO: may be problem. Check it later - - Optional container = room == null? - Optional.empty() : - Optional.of(node2roomsMap.get(node).get(room.getIdentifier())); - - if (container.isPresent()) { - var cont = container.get(); - var addedUsers = userRepository.linkWithRoom(cont.room(), users, false); - cont.used().getAndAdd(addedUsers); - } - - return container.map(RoomContainer::room); - } finally { - lock.writeLock().unlock(); - } - } - - @Override - public void onCreateNode(Node node) { - lock.writeLock().lock(); - try { - node2roomsMap.put(node, new Rooms()); - } finally { - lock.writeLock().unlock(); - } - } - - @Override - public List onRemoveNode(Node node) { - lock.writeLock().lock(); - try { - var deleted = node2roomsMap.get(node).values().stream().map(container -> container.room).toList(); - node2roomsMap.remove(node); - - return deleted; - } finally { - lock.writeLock().unlock(); - } - } - - private record RoomContainer(Room room, AtomicInteger used) { - - public boolean isAvailable(int requiredSlots) { - return room.isAvailable(used.get(), requiredSlots); - } + public Room pick(Node node, Set users) throws NoRoomsAvailableException { + return containerRepository.findById(node.getIdentifier()) + .orElseThrow(() -> new NodeNotFoundException(node.getIdentifier())) + .pick(users); } - - private static class Rooms extends LinkedHashMap {} } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/UserRepositoryImpl.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/UserRepositoryImpl.java index 63ee5ee..6ffdf9b 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/UserRepositoryImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/UserRepositoryImpl.java @@ -1,207 +1,104 @@ package ru.dragonestia.picker.repository.impl; +import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Component; +import ru.dragonestia.picker.api.exception.NodeNotFoundException; import ru.dragonestia.picker.api.exception.RoomAreFullException; +import ru.dragonestia.picker.api.exception.RoomNotFoundException; import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.repository.UserRepository; -import ru.dragonestia.picker.repository.impl.cache.NodeId2PickerModeCache; -import ru.dragonestia.picker.repository.impl.picker.LeastPickedPicker; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -@Repository +@Component @RequiredArgsConstructor public class UserRepositoryImpl implements UserRepository { - private final NodeId2PickerModeCache nodeId2PickerModeCache; - private final Map> usersMap = new ConcurrentHashMap<>(); - private final Map> roomUsers = new ConcurrentHashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(true); + private final ContainerRepository containerRepository; - @Override - public int linkWithRoom(Room room, Collection users, boolean force) throws RoomAreFullException { - var toAdd = new HashSet(); - - lock.writeLock().lock(); - try { - var path = new NodeRoomPath(room.getNodeIdentifier(), room.getIdentifier()); - var usersSet = roomUsers.getOrDefault(path, new HashSet<>()); + private final Map> userRooms = new ConcurrentHashMap<>(); - if (!force && !room.hasUnlimitedSlots()) { - if (room.getMaxSlots() < usersSet.size() + users.size()) { - throw new RoomAreFullException(room.getNodeIdentifier(), room.getIdentifier()); + @PostConstruct + void init() { + containerRepository.setTransactionListener(transaction -> { + synchronized (userRooms) { + for (var user: transaction.target()) { + var set = userRooms.computeIfAbsent(user, k -> new HashSet<>()); + set.add(transaction.room()); } } + }); + } - users.forEach(user -> { - var set = usersMap.getOrDefault(user, new HashSet<>()); - if (!set.contains(room)) { - toAdd.add(user); - set.add(room); - } - usersMap.put(user, set); - }); - - usersSet.addAll(toAdd); - roomUsers.put(path, usersSet); + @Override + public void linkWithRoom(Room room, Collection users, boolean force) throws RoomAreFullException { + synchronized (userRooms) { + getRoomContainer(room).addUsers(users, force); - var picker = nodeId2PickerModeCache.get(room.getNodeIdentifier()); - if (picker instanceof LeastPickedPicker leastPickedPicker) { - leastPickedPicker.updateUsersAmount(room, roomUsers.get(path).size()); + for (var user: users) { + var set = userRooms.computeIfAbsent(user, k -> new HashSet<>()); + set.add(room); } - } finally { - lock.writeLock().unlock(); } - return toAdd.size(); } @Override public void unlinkWithRoom(Room room, Collection users) { - var counter = new AtomicInteger(); - - lock.writeLock().lock(); - try { - usersMap.forEach((user, set) -> { - if (!set.contains(room)) return; + synchronized (userRooms) { + getRoomContainer(room).removeUsers(users); + for (var user: users) { + var set = userRooms.get(user); + if (set == null) continue; set.remove(room); - counter.incrementAndGet(); - - if (set.isEmpty()) { - usersMap.remove(user); - } - }); - - var path = new NodeRoomPath(room.getNodeIdentifier(), room.getIdentifier()); - var set = roomUsers.getOrDefault(path, new HashSet<>()); - set.removeAll(users); - if (set.isEmpty()) { - roomUsers.remove(path); - } else { - roomUsers.put(path, set); + if (set.isEmpty()) userRooms.remove(user); } - - var picker = nodeId2PickerModeCache.get(room.getNodeIdentifier()); - if (picker instanceof LeastPickedPicker leastPickedPicker) { - leastPickedPicker.updateUsersAmount(room, set.size()); - } - } finally { - lock.writeLock().unlock(); } - counter.get(); } @Override - public List findAllLinkedUserRooms(User user) { - lock.writeLock().lock(); - try { - return usersMap.getOrDefault(user, new HashSet<>()).stream().toList(); - } finally { - lock.writeLock().unlock(); - } + public Collection findAllLinkedUserRooms(User user) { + return Collections.unmodifiableSet(userRooms.get(user)); } @Override - public void onRemoveRoom(Room room) { - lock.writeLock().lock(); - try { - var users = roomUsers.remove(new NodeRoomPath(room.getNodeIdentifier(), room.getIdentifier())); - - if (users == null) return; - - users.forEach(user -> { - var set = usersMap.getOrDefault(user, new HashSet<>()); - set.remove(room); - - if (set.isEmpty()) { - usersMap.remove(user); - } - }); - } finally { - lock.writeLock().unlock(); - } - } - - @Override - public List usersOf(Room room) { - lock.readLock().lock(); - try { - return roomUsers.getOrDefault(new NodeRoomPath(room.getNodeIdentifier(), room.getIdentifier()), new HashSet<>()) - .stream() - .toList(); - } finally { - lock.readLock().unlock(); - } + public Collection usersOf(Room room) { + return getRoomContainer(room).allUsers(); } @Override - public List search(String input) { - lock.readLock().lock(); - try { - return usersMap.keySet().stream() - .filter(user -> user.getIdentifier().startsWith(input)) - .sorted(Comparator.comparing(User::getIdentifier)) - .toList(); - } finally { - lock.readLock().unlock(); - } + public Collection search(String input) { + return userRooms.keySet().stream().filter(user -> user.getIdentifier().startsWith(input)).toList(); } @Override public int countAllUsers() { - lock.readLock().lock(); - try { - return usersMap.size(); - } finally { - lock.readLock().unlock(); - } + return userRooms.size(); } @Override public Map countUsersForNodes() { - var map = new HashMap>(); - - lock.readLock().lock(); - try { - roomUsers.forEach((path, users) -> { - if (map.containsKey(path.node)) { - map.get(path.node).addAll(users); - return; - } + var result = new HashMap(); - map.put(path.node, new HashSet<>(users)); - }); - } finally { - lock.readLock().unlock(); - } + containerRepository.all().forEach(nodeContainer -> { + var nodeId = nodeContainer.getNode().getIdentifier(); - var result = new HashMap(); - map.forEach((node, users) -> result.put(node, users.size())); + nodeContainer.allRooms().forEach(roomContainer -> { + result.put(nodeId, result.getOrDefault(nodeId, 0) + roomContainer.countUsers()); + }); + }); return result; } - private record NodeRoomPath(String node, String room) { - - @Override - public int hashCode() { - return Objects.hash(node, room); - } - - @Override - public boolean equals(Object o) { - if (o == null) return false; - if (o == this) return true; - if (o instanceof NodeRoomPath other) { - return other.node().equals(node()) && other.room().equals(room()); - } - return false; - } + private RoomContainer getRoomContainer(Room room) { + return containerRepository.findById(room.getNodeIdentifier()) + .orElseThrow(() -> new NodeNotFoundException(room.getNodeIdentifier())) + .findRoomById(room.getIdentifier()) + .orElseThrow(() -> new RoomNotFoundException(room.getNodeIdentifier(), room.getIdentifier())); } } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/cache/NodeId2PickerModeCache.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/cache/NodeId2PickerModeCache.java deleted file mode 100644 index 4e1a005..0000000 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/cache/NodeId2PickerModeCache.java +++ /dev/null @@ -1,25 +0,0 @@ -package ru.dragonestia.picker.repository.impl.cache; - -import org.springframework.stereotype.Component; -import ru.dragonestia.picker.repository.impl.picker.RoomPicker; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Component -public class NodeId2PickerModeCache { - - private final Map cache = new ConcurrentHashMap<>(); - - public void put(String nodeId, RoomPicker picker) { - cache.put(nodeId, picker); - } - - public void remove(String nodeId) { - cache.remove(nodeId); - } - - public RoomPicker get(String nodeId) { - return cache.get(nodeId); - } -} diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java new file mode 100644 index 0000000..cf7fa9b --- /dev/null +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java @@ -0,0 +1,104 @@ +package ru.dragonestia.picker.repository.impl.container; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.picker.api.exception.RoomAlreadyExistException; +import ru.dragonestia.picker.model.Node; +import ru.dragonestia.picker.model.Room; +import ru.dragonestia.picker.model.User; +import ru.dragonestia.picker.repository.impl.picker.LeastPickedPicker; +import ru.dragonestia.picker.repository.impl.picker.RoomPicker; +import ru.dragonestia.picker.repository.impl.picker.RoundRobinPicker; +import ru.dragonestia.picker.repository.impl.picker.SequentialFillingPicker; +import ru.dragonestia.picker.repository.impl.type.UserTransaction; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class NodeContainer { + + @Getter + private final Node node; + private final UserTransaction.Listener transactionListener; + private final RoomPicker picker; + + private final ReadWriteLock roomLock = new ReentrantReadWriteLock(); + private final Map rooms = new ConcurrentHashMap<>(); + + public NodeContainer(@NotNull Node node, @NotNull UserTransaction.Listener transactionListener) { + this.node = node; + this.transactionListener = transactionListener; + this.picker = initPicker(); + } + + private @NotNull RoomPicker initPicker() { + return switch (node.getPickingMethod()) { + case SEQUENTIAL_FILLING -> new SequentialFillingPicker(); + case ROUND_ROBIN -> new RoundRobinPicker(); + case LEAST_PICKED -> new LeastPickedPicker(); + }; + } + + public void addRoom(Room room) throws RoomAlreadyExistException { + roomLock.writeLock().lock(); + try { + if (rooms.containsKey(room.getIdentifier())) { + throw new RoomAlreadyExistException(node.getIdentifier(), room.getIdentifier()); + } + + var container = new RoomContainer(room, this); + rooms.put(room.getIdentifier(), container); + picker.add(container); + } finally { + roomLock.writeLock().unlock(); + } + } + + public void removeRoom(@NotNull Room room) { + roomLock.writeLock().lock(); + try { + picker.remove(rooms.remove(room.getIdentifier())); + } finally { + roomLock.writeLock().unlock(); + } + } + + public void removeRoomsByIds(@NotNull Collection roomIds) { + roomLock.writeLock().lock(); + try { + roomIds.forEach(roomId -> picker.remove(rooms.remove(roomId))); + } finally { + roomLock.writeLock().unlock(); + } + } + + public @NotNull Optional findRoomById(@NotNull String roomId) { + roomLock.readLock().lock(); + try { + return Optional.ofNullable(rooms.get(roomId)); + } finally { + roomLock.readLock().unlock(); + } + } + + public @NotNull Collection allRooms() { + roomLock.readLock().lock(); + try { + return rooms.values(); + } finally { + roomLock.readLock().unlock(); + } + } + + public @NotNull Room pick(@NotNull Set users) { + var room = picker.pick(users); + transactionListener.accept(new UserTransaction(room.getRoom(), users)); + return room.getRoom(); + } + + public @NotNull RoomPicker getPicker() { + return picker; + } +} diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java new file mode 100644 index 0000000..285f3ef --- /dev/null +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java @@ -0,0 +1,105 @@ +package ru.dragonestia.picker.repository.impl.container; + +import lombok.Getter; +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.picker.api.exception.RoomAreFullException; +import ru.dragonestia.picker.model.Room; +import ru.dragonestia.picker.model.User; +import ru.dragonestia.picker.repository.impl.picker.LeastPickedPicker; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class RoomContainer { + + @Getter + private final Room room; + private final NodeContainer container; + + private final ReadWriteLock usersLock = new ReentrantReadWriteLock(); + private final Set users = new HashSet<>(); + + public RoomContainer(@NotNull Room room, @NotNull NodeContainer container) { + this.room = room; + this.container = container; + } + + public void addUsers(@NotNull Collection toAdd, boolean force) { + usersLock.writeLock().lock(); + try { + if (force || canAdd0(toAdd.size())) { + users.addAll(toAdd); + noticePickersAboutUserNumberUpdate(); + } else { + throw new RoomAreFullException(room.getNodeIdentifier(), room.getIdentifier()); + } + } finally { + usersLock.writeLock().unlock(); + } + } + + public void removeUsers(@NotNull Collection toRemove) { + usersLock.writeLock().lock(); + try { + users.removeAll(toRemove); + noticePickersAboutUserNumberUpdate(); + } finally { + usersLock.writeLock().unlock(); + } + } + + public @NotNull Collection removeAllUsers() { + usersLock.writeLock().lock(); + try { + var set = new HashSet<>(users); + users.clear(); + noticePickersAboutUserNumberUpdate(); + return set; + } finally { + usersLock.writeLock().unlock(); + } + } + + public @NotNull Collection allUsers() { + usersLock.readLock().lock(); + try { + return Collections.unmodifiableSet(users); + } finally { + usersLock.readLock().unlock(); + } + } + + public int countUsers() { + return users.size(); + } + + private boolean canAdd0(int users) { + return room.hasUnlimitedSlots() || users + countUsers() <= room.getMaxSlots(); + } + + public boolean canAdd(int users) { + try { + return canAdd0(users); + } finally { + usersLock.readLock().unlock(); + } + } + + public boolean canBePicked(int users) { + try { + return !room.isLocked() && canAdd0(users); + } finally { + usersLock.readLock().unlock(); + } + } + + private void noticePickersAboutUserNumberUpdate() { + if (container.getPicker() instanceof LeastPickedPicker picker) { + picker.updateUsersAmount(room, countUsers()); + } + } +} diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java index 4a81985..0ad981c 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java @@ -5,34 +5,30 @@ import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.impl.collection.DynamicSortedMap; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.Collection; public class LeastPickedPicker implements RoomPicker { - private final UserRepository userRepository; private final DynamicSortedMap map = new DynamicSortedMap<>(); - public LeastPickedPicker(UserRepository userRepository) { - this.userRepository = userRepository; - } - @Override - public void add(Room room) { + public void add(RoomContainer container) { synchronized (map) { - map.put(new RoomWrapper(room, () -> userRepository.usersOf(room).size())); + map.put(new RoomWrapper(container)); } } @Override - public void remove(Room room) { + public void remove(RoomContainer container) { synchronized (map) { - map.removeById(room.getIdentifier()); + map.removeById(container.getRoom().getIdentifier()); } } @Override - public Room pick(Collection users) { + public RoomContainer pick(Collection users) { RoomWrapper wrapper; synchronized (map) { diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomPicker.java index b3df329..4e2d2a5 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomPicker.java @@ -1,10 +1,10 @@ package ru.dragonestia.picker.repository.impl.picker; import ru.dragonestia.picker.api.model.node.PickingMethod; -import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.User; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; -public interface RoomPicker extends Picker { +public interface RoomPicker extends Picker { PickingMethod getPickingMode(); } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomWrapper.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomWrapper.java index 1d0db9f..ba0dcc2 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomWrapper.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoomWrapper.java @@ -1,46 +1,43 @@ package ru.dragonestia.picker.repository.impl.picker; -import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.repository.impl.collection.QueuedLinkedList; import ru.dragonestia.picker.repository.impl.collection.DynamicSortedMap; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.function.Consumer; -import java.util.function.Supplier; -public class RoomWrapper implements ItemWrapper, QueuedLinkedList.Item, DynamicSortedMap.Item { +public class RoomWrapper implements ItemWrapper, QueuedLinkedList.Item, DynamicSortedMap.Item { - private final Room room; - private final Supplier userCountSupplier; + private final RoomContainer container; private Consumer setter; - public RoomWrapper(Room room, Supplier userCountSupplier) { - this.room = room; - this.userCountSupplier = userCountSupplier; + public RoomWrapper(RoomContainer container) { + this.container = container; } @Override public String getId() { - return room.getIdentifier(); + return container.getRoom().getIdentifier(); } @Override public int countUnits() { - return userCountSupplier.get(); + return container.countUsers(); } @Override public int maxUnits() { - return room.getMaxSlots(); + return container.getRoom().getMaxSlots(); } @Override - public Room getItem() { - return room; + public RoomContainer getItem() { + return container; } @Override public boolean canAddUnits(int amount) { - return ItemWrapper.super.canAddUnits(amount) && !room.isLocked(); + return container.canBePicked(amount); } @Override diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java index 3902619..48906db 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java @@ -1,40 +1,34 @@ package ru.dragonestia.picker.repository.impl.picker; import ru.dragonestia.picker.api.model.node.PickingMethod; -import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.User; -import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.impl.collection.QueuedLinkedList; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; public class RoundRobinPicker implements RoomPicker { - private final UserRepository userRepository; private final AtomicInteger addition = new AtomicInteger(0); private final QueuedLinkedList list = new QueuedLinkedList<>(wrapper -> wrapper.canAddUnits(addition.get())); - public RoundRobinPicker(UserRepository userRepository) { - this.userRepository = userRepository; - } - @Override - public void add(Room room) { + public void add(RoomContainer container) { synchronized (list) { - list.add(new RoomWrapper(room, () -> userRepository.usersOf(room).size())); + list.add(new RoomWrapper(container)); } } @Override - public void remove(Room room) { + public void remove(RoomContainer container) { synchronized (list) { - list.removeById(room.getIdentifier()); + list.removeById(container.getRoom().getIdentifier()); } } @Override - public Room pick(Collection users) { + public RoomContainer pick(Collection users) { int amount = users.size(); RoomWrapper wrapper; diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java index 9cc221c..a91b27a 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java @@ -1,9 +1,8 @@ package ru.dragonestia.picker.repository.impl.picker; import ru.dragonestia.picker.api.model.node.PickingMethod; -import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.User; -import ru.dragonestia.picker.repository.UserRepository; +import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.Collection; import java.util.LinkedHashMap; @@ -11,29 +10,24 @@ public class SequentialFillingPicker implements RoomPicker { - private final UserRepository userRepository; private final Map wrappers = new LinkedHashMap<>(); - public SequentialFillingPicker(UserRepository userRepository) { - this.userRepository = userRepository; - } - @Override - public void add(Room room) { + public void add(RoomContainer container) { synchronized (wrappers) { - wrappers.put(room.getIdentifier(), new RoomWrapper(room, () -> userRepository.usersOf(room).size())); + wrappers.put(container.getRoom().getIdentifier(), new RoomWrapper(container)); } } @Override - public void remove(Room room) { + public void remove(RoomContainer container) { synchronized (wrappers) { - wrappers.remove(room.getIdentifier()); + wrappers.remove(container.getRoom().getIdentifier()); } } @Override - public Room pick(Collection users) { + public RoomContainer pick(Collection users) { int amount = users.size(); synchronized (wrappers) { diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/type/UserTransaction.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/type/UserTransaction.java new file mode 100644 index 0000000..1ef9473 --- /dev/null +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/type/UserTransaction.java @@ -0,0 +1,13 @@ +package ru.dragonestia.picker.repository.impl.type; + +import org.jetbrains.annotations.NotNull; +import ru.dragonestia.picker.model.Room; +import ru.dragonestia.picker.model.User; + +import java.util.Collection; +import java.util.function.Consumer; + +public record UserTransaction(@NotNull Room room, Collection target) { + + public interface Listener extends Consumer {} +} diff --git a/server/src/main/java/ru/dragonestia/picker/service/RoomService.java b/server/src/main/java/ru/dragonestia/picker/service/RoomService.java index 23d39b7..780ee8e 100644 --- a/server/src/main/java/ru/dragonestia/picker/service/RoomService.java +++ b/server/src/main/java/ru/dragonestia/picker/service/RoomService.java @@ -9,6 +9,7 @@ import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.User; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; @@ -21,11 +22,11 @@ public interface RoomService { Optional find(Node node, String roomId); - List all(Node node); + Collection all(Node node); List getAllRoomsWithDetailsResponse(Node node, Set details); - PickedRoomResponse pickAvailable(Node node, List users); + PickedRoomResponse pickAvailable(Node node, Set users); void updateState(Room room); } diff --git a/server/src/main/java/ru/dragonestia/picker/service/UserService.java b/server/src/main/java/ru/dragonestia/picker/service/UserService.java index be7644f..faafa0f 100644 --- a/server/src/main/java/ru/dragonestia/picker/service/UserService.java +++ b/server/src/main/java/ru/dragonestia/picker/service/UserService.java @@ -14,7 +14,7 @@ public interface UserService { - List getUserRooms(User user); + Collection getUserRooms(User user); List getUserRoomsWithDetails(User user, Set details); @@ -22,7 +22,7 @@ public interface UserService { void unlinkUsersFromRoom(Room room, Collection users); - List getRoomUsers(Room room); + Collection getRoomUsers(Room room); List getRoomUsersWithDetailsResponse(Room room, Set details); diff --git a/server/src/main/java/ru/dragonestia/picker/service/impl/NodeServiceImpl.java b/server/src/main/java/ru/dragonestia/picker/service/impl/NodeServiceImpl.java index 3ec93ed..7a4949a 100644 --- a/server/src/main/java/ru/dragonestia/picker/service/impl/NodeServiceImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/service/impl/NodeServiceImpl.java @@ -8,6 +8,7 @@ import ru.dragonestia.picker.api.model.node.ResponseNode; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.repository.NodeRepository; +import ru.dragonestia.picker.repository.RoomRepository; import ru.dragonestia.picker.service.NodeService; import ru.dragonestia.picker.storage.NodeAndRoomStorage; import ru.dragonestia.picker.util.DetailsExtractor; @@ -23,22 +24,24 @@ public class NodeServiceImpl implements NodeService { private final NodeRepository nodeRepository; + private final RoomRepository roomRepository; private final DetailsExtractor detailsExtractor; private final NamingValidator namingValidator; private final NodeAndRoomStorage storage; @Override public void create(Node node) throws InvalidNodeIdentifierException, NodeAlreadyExistException { - namingValidator.validateNodeId(node.getIdentifier()); nodeRepository.create(node); storage.saveNode(node); } @Override public void remove(Node node) { - for (var room: nodeRepository.delete(node)) { + for (var room: roomRepository.all(node)) { storage.removeRoom(room); } + + nodeRepository.delete(node); storage.removeNode(node); } @@ -58,6 +61,6 @@ public List getAllNodesWithDetailsResponse(Set detail @Override public Optional find(String nodeId) { - return nodeRepository.find(nodeId); + return nodeRepository.findById(nodeId); } } diff --git a/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java b/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java index eccfe3d..3d4a6b4 100644 --- a/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/service/impl/RoomServiceImpl.java @@ -21,10 +21,7 @@ import ru.dragonestia.picker.util.DetailsExtractor; import ru.dragonestia.picker.util.NamingValidator; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @Log4j2 @@ -43,7 +40,7 @@ public class RoomServiceImpl implements RoomService { public void create(Room room) throws InvalidRoomIdentifierException, RoomAlreadyExistException, NotPersistedNodeException { namingValidator.validateRoomId(room.getNodeIdentifier(), room.getIdentifier()); - var node = nodeRepository.find(room.getNodeIdentifier()).orElseThrow(() -> new NodeNotFoundException(room.getNodeIdentifier())); + var node = nodeRepository.findById(room.getNodeIdentifier()).orElseThrow(() -> new NodeNotFoundException(room.getNodeIdentifier())); if (!node.isPersist() && room.isPersist()) { throw new NotPersistedNodeException(node.getIdentifier(), room.getIdentifier()); } @@ -64,7 +61,7 @@ public Optional find(Node node, String roomId) { } @Override - public List all(Node node) { + public Collection all(Node node) { return roomRepository.all(node); } @@ -78,9 +75,8 @@ public List getAllRoomsWithDetailsResponse(Node node, Set users) { - var room = roomRepository.pickFree(node, users) - .orElseThrow(() -> new RuntimeException("There are no rooms available. Given users count: " + users.size())); + public PickedRoomResponse pickAvailable(Node node, Set users) { + var room = roomRepository.pick(node, users); var roomUsers = userRepository.usersOf(room); return new PickedRoomResponse( diff --git a/server/src/main/java/ru/dragonestia/picker/service/impl/UserServiceImpl.java b/server/src/main/java/ru/dragonestia/picker/service/impl/UserServiceImpl.java index 0858493..d2848fa 100644 --- a/server/src/main/java/ru/dragonestia/picker/service/impl/UserServiceImpl.java +++ b/server/src/main/java/ru/dragonestia/picker/service/impl/UserServiceImpl.java @@ -23,7 +23,7 @@ public class UserServiceImpl implements UserService { private final DetailsExtractor detailsExtractor; @Override - public List getUserRooms(User user) { + public Collection getUserRooms(User user) { return userRepository.findAllLinkedUserRooms(user); } @@ -47,7 +47,7 @@ public void unlinkUsersFromRoom(Room room, Collection users) { } @Override - public List getRoomUsers(Room room) { + public Collection getRoomUsers(Room room) { return userRepository.usersOf(room); } diff --git a/server/src/main/java/ru/dragonestia/picker/util/NamingValidator.java b/server/src/main/java/ru/dragonestia/picker/util/NamingValidator.java index d49c263..52783a2 100644 --- a/server/src/main/java/ru/dragonestia/picker/util/NamingValidator.java +++ b/server/src/main/java/ru/dragonestia/picker/util/NamingValidator.java @@ -8,6 +8,7 @@ import ru.dragonestia.picker.api.util.IdentifierValidator; import ru.dragonestia.picker.model.User; +import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -30,7 +31,7 @@ public boolean validateUserId(String input) { return IdentifierValidator.forUser(input); } - public List validateUserIds(List input) throws InvalidUsernamesException { + public void validateUserIds(Collection input) throws InvalidUsernamesException { var users = new LinkedList(); var invalid = new LinkedList(); @@ -44,9 +45,7 @@ public List validateUserIds(List input) throws InvalidUsernamesExc } if (!invalid.isEmpty()) { - throw new InvalidUsernamesException(input, invalid); + throw new InvalidUsernamesException(input.stream().toList(), invalid); } - - return users; } } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java b/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java index b8df440..5b2dab3 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java @@ -40,7 +40,7 @@ public class LeastPickedTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { - var roomOpt = roomRepository.pickFree(node, userFiller.createRandomUsers(usersAmount)); + var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); Assertions.assertTrue(roomOpt.isPresent()); var room = roomOpt.get(); @@ -73,7 +73,7 @@ public Stream provideArguments(ExtensionContext extensionCo @Timeout(value = 1, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) @Test void testNoOneRoomExpected() { // Take 9 users. expected none result - var roomOpt = roomRepository.pickFree(node, userFiller.createRandomUsers(9)); + var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(9)); Assertions.assertTrue(roomOpt.isEmpty()); } } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java b/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java index ec23f94..6bc5684 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java @@ -8,7 +8,6 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; -import ru.dragonestia.picker.api.model.node.INode; import ru.dragonestia.picker.config.FillingNodesConfig; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.repository.RoomRepository; @@ -39,7 +38,7 @@ public class RoundRobinTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { - var roomOpt = roomRepository.pickFree(node, userFiller.createRandomUsers(usersAmount)); + var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); Assertions.assertTrue(roomOpt.isPresent()); var room = roomOpt.get(); @@ -69,7 +68,7 @@ public Stream provideArguments(ExtensionContext extensionCo @Timeout(value = 1, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) @Test void testNoOneRoomExpected() { // Take 9 users. expected none result - var roomOpt = roomRepository.pickFree(node, userFiller.createRandomUsers(9)); + var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(9)); Assertions.assertTrue(roomOpt.isEmpty()); } } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java b/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java index 61fb5c8..3cac375 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java @@ -40,7 +40,7 @@ public class SequentialFillingTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { - var roomOpt = roomRepository.pickFree(node, userFiller.createRandomUsers(usersAmount)); + var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); Assertions.assertTrue(roomOpt.isPresent()); var room = roomOpt.get(); @@ -70,7 +70,7 @@ public Stream provideArguments(ExtensionContext extensionCo @Timeout(value = 1, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) @Test void testNoOneRoomExpected() { // Take 9 users. expected none result - var roomOpt = roomRepository.pickFree(node, userFiller.createRandomUsers(9)); + var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(9)); Assertions.assertTrue(roomOpt.isEmpty()); } } From a5838f4be1d096f8b2fda5df18d6197a76c5de79 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 15:43:47 +0700 Subject: [PATCH 05/13] Tried to fix test, but LeastPicker and SequentialFilling do no passing --- .../repository/impl/container/NodeContainer.java | 14 ++++++++------ .../repository/impl/container/RoomContainer.java | 1 + .../repository/impl/picker/LeastPickedPicker.java | 7 ++++++- .../repository/impl/picker/RoundRobinPicker.java | 7 ++++++- .../impl/picker/SequentialFillingPicker.java | 7 ++++++- .../picker/picker/LeastPickedTests.java | 10 ++++------ .../dragonestia/picker/picker/RoundRobinTests.java | 9 +++------ .../picker/picker/SequentialFillingTests.java | 10 ++++------ .../picker/service/RoomServiceTests.java | 5 +++-- .../ru/dragonestia/picker/util/UserFiller.java | 12 +++++------- 10 files changed, 46 insertions(+), 36 deletions(-) diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java index cf7fa9b..e436b90 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java @@ -35,9 +35,9 @@ public NodeContainer(@NotNull Node node, @NotNull UserTransaction.Listener trans private @NotNull RoomPicker initPicker() { return switch (node.getPickingMethod()) { - case SEQUENTIAL_FILLING -> new SequentialFillingPicker(); - case ROUND_ROBIN -> new RoundRobinPicker(); - case LEAST_PICKED -> new LeastPickedPicker(); + case SEQUENTIAL_FILLING -> new SequentialFillingPicker(this); + case ROUND_ROBIN -> new RoundRobinPicker(this); + case LEAST_PICKED -> new LeastPickedPicker(this); }; } @@ -93,9 +93,11 @@ public void removeRoomsByIds(@NotNull Collection roomIds) { } public @NotNull Room pick(@NotNull Set users) { - var room = picker.pick(users); - transactionListener.accept(new UserTransaction(room.getRoom(), users)); - return room.getRoom(); + synchronized (picker) { + var room = picker.pick(users); + transactionListener.accept(new UserTransaction(room.getRoom(), users)); + return room.getRoom(); + } } public @NotNull RoomPicker getPicker() { diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java index 285f3ef..bf7d60f 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java @@ -90,6 +90,7 @@ public boolean canAdd(int users) { } public boolean canBePicked(int users) { + usersLock.readLock().lock(); try { return !room.isLocked() && canAdd0(users); } finally { diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java index 0ad981c..aa6863c 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/LeastPickedPicker.java @@ -1,16 +1,21 @@ package ru.dragonestia.picker.repository.impl.picker; +import lombok.RequiredArgsConstructor; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.api.model.node.PickingMethod; import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.repository.UserRepository; import ru.dragonestia.picker.repository.impl.collection.DynamicSortedMap; +import ru.dragonestia.picker.repository.impl.container.NodeContainer; import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.Collection; +@RequiredArgsConstructor public class LeastPickedPicker implements RoomPicker { + private final NodeContainer container; private final DynamicSortedMap map = new DynamicSortedMap<>(); @Override @@ -37,7 +42,7 @@ public RoomContainer pick(Collection users) { if (!wrapper.canAddUnits(users.size())) throw new RuntimeException(); } catch (RuntimeException ex) { - throw new RuntimeException("There are no rooms available"); + throw new NoRoomsAvailableException(container.getNode().getIdentifier()); } } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java index 48906db..f423cb0 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/RoundRobinPicker.java @@ -1,15 +1,20 @@ package ru.dragonestia.picker.repository.impl.picker; +import lombok.RequiredArgsConstructor; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.api.model.node.PickingMethod; import ru.dragonestia.picker.model.User; import ru.dragonestia.picker.repository.impl.collection.QueuedLinkedList; +import ru.dragonestia.picker.repository.impl.container.NodeContainer; import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.Collection; import java.util.concurrent.atomic.AtomicInteger; +@RequiredArgsConstructor public class RoundRobinPicker implements RoomPicker { + private final NodeContainer container; private final AtomicInteger addition = new AtomicInteger(0); private final QueuedLinkedList list = new QueuedLinkedList<>(wrapper -> wrapper.canAddUnits(addition.get())); @@ -37,7 +42,7 @@ public RoomContainer pick(Collection users) { addition.set(amount); wrapper = list.pick(); } catch (RuntimeException ex) { - throw new RuntimeException("There are no rooms available"); + throw new NoRoomsAvailableException(container.getNode().getIdentifier()); } } diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java index a91b27a..ca954f5 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/picker/SequentialFillingPicker.java @@ -1,15 +1,20 @@ package ru.dragonestia.picker.repository.impl.picker; +import lombok.RequiredArgsConstructor; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.api.model.node.PickingMethod; import ru.dragonestia.picker.model.User; +import ru.dragonestia.picker.repository.impl.container.NodeContainer; import ru.dragonestia.picker.repository.impl.container.RoomContainer; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; +@RequiredArgsConstructor public class SequentialFillingPicker implements RoomPicker { + private final NodeContainer container; private final Map wrappers = new LinkedHashMap<>(); @Override @@ -38,7 +43,7 @@ public RoomContainer pick(Collection users) { } } - throw new RuntimeException("There are no rooms available"); + throw new NoRoomsAvailableException(container.getNode().getIdentifier()); } @Override diff --git a/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java b/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java index 5b2dab3..3256668 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java @@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.config.FillingNodesConfig; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.repository.RoomRepository; @@ -40,14 +41,12 @@ public class LeastPickedTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { - var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); - Assertions.assertTrue(roomOpt.isPresent()); - - var room = roomOpt.get(); + var room = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); var slots = room.getMaxSlots(); var users = userRepository.usersOf(room); Assertions.assertTrue(slots == -1 || slots >= users.size()); // check slots limitation + System.out.printf("Room(%s) has %s/%s users. Expected: %s, added: %s%n", room.getIdentifier(), users.size(), slots, expectedRoomId, usersAmount); Assertions.assertEquals(expectedRoomId, room.getIdentifier()); } @@ -73,7 +72,6 @@ public Stream provideArguments(ExtensionContext extensionCo @Timeout(value = 1, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) @Test void testNoOneRoomExpected() { // Take 9 users. expected none result - var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(9)); - Assertions.assertTrue(roomOpt.isEmpty()); + Assertions.assertThrows(NoRoomsAvailableException.class, () -> roomRepository.pick(node, userFiller.createRandomUsers(9))); } } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java b/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java index 6bc5684..5ab780d 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/RoundRobinTests.java @@ -8,6 +8,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.config.FillingNodesConfig; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.repository.RoomRepository; @@ -38,10 +39,7 @@ public class RoundRobinTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { - var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); - Assertions.assertTrue(roomOpt.isPresent()); - - var room = roomOpt.get(); + var room = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); var slots = room.getMaxSlots(); var users = userRepository.usersOf(room); Assertions.assertTrue(slots == -1 || slots >= users.size()); // check slots limitation @@ -68,7 +66,6 @@ public Stream provideArguments(ExtensionContext extensionCo @Timeout(value = 1, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) @Test void testNoOneRoomExpected() { // Take 9 users. expected none result - var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(9)); - Assertions.assertTrue(roomOpt.isEmpty()); + Assertions.assertThrows(NoRoomsAvailableException.class, () -> roomRepository.pick(node, userFiller.createRandomUsers(9))); } } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java b/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java index 3cac375..9ece42d 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java @@ -10,6 +10,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Import; +import ru.dragonestia.picker.api.exception.NoRoomsAvailableException; import ru.dragonestia.picker.config.FillingNodesConfig; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.repository.RoomRepository; @@ -40,14 +41,12 @@ public class SequentialFillingTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { - var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); - Assertions.assertTrue(roomOpt.isPresent()); - - var room = roomOpt.get(); + var room = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); var slots = room.getMaxSlots(); var users = userRepository.usersOf(room); Assertions.assertTrue(slots == -1 || slots >= users.size()); // check slots limitation + System.out.printf("Room(%s) has %s/%s users. Expected: %s, added: %s%n", room.getIdentifier(), users.size(), slots, expectedRoomId, usersAmount); Assertions.assertEquals(expectedRoomId, room.getIdentifier()); } @@ -70,7 +69,6 @@ public Stream provideArguments(ExtensionContext extensionCo @Timeout(value = 1, threadMode = Timeout.ThreadMode.SEPARATE_THREAD) @Test void testNoOneRoomExpected() { // Take 9 users. expected none result - var roomOpt = roomRepository.pick(node, userFiller.createRandomUsers(9)); - Assertions.assertTrue(roomOpt.isEmpty()); + Assertions.assertThrows(NoRoomsAvailableException.class, () -> roomRepository.pick(node, userFiller.createRandomUsers(9))); } } diff --git a/server/src/test/java/ru/dragonestia/picker/service/RoomServiceTests.java b/server/src/test/java/ru/dragonestia/picker/service/RoomServiceTests.java index 304e87a..0968f54 100644 --- a/server/src/test/java/ru/dragonestia/picker/service/RoomServiceTests.java +++ b/server/src/test/java/ru/dragonestia/picker/service/RoomServiceTests.java @@ -21,6 +21,7 @@ import ru.dragonestia.picker.model.type.SlotLimit; import java.util.List; +import java.util.Set; @SpringBootTest public class RoomServiceTests { @@ -93,7 +94,7 @@ void test_pickRoom() { rooms.forEach(room -> roomService.create(room)); - var users = List.of( + var users = Set.of( new User(UserIdentifier.of("1")), new User(UserIdentifier.of("2")), new User(UserIdentifier.of("3")), @@ -121,6 +122,6 @@ void test_nodeDoesNotExists() { Assertions.assertThrows(NodeNotFoundException.class, () -> roomService.create(room)); Assertions.assertThrows(NodeNotFoundException.class, () -> roomService.remove(room)); Assertions.assertThrows(NodeNotFoundException.class, () -> roomService.find(node, "Bruh")); - Assertions.assertThrows(NodeNotFoundException.class, () -> roomService.pickAvailable(node, List.of(new User(UserIdentifier.of("1"))))); + Assertions.assertThrows(NodeNotFoundException.class, () -> roomService.pickAvailable(node, Set.of(new User(UserIdentifier.of("1"))))); } } diff --git a/server/src/test/java/ru/dragonestia/picker/util/UserFiller.java b/server/src/test/java/ru/dragonestia/picker/util/UserFiller.java index 0c17815..1f6f53f 100644 --- a/server/src/test/java/ru/dragonestia/picker/util/UserFiller.java +++ b/server/src/test/java/ru/dragonestia/picker/util/UserFiller.java @@ -4,18 +4,16 @@ import ru.dragonestia.picker.api.repository.type.UserIdentifier; import ru.dragonestia.picker.model.User; -import java.util.LinkedList; -import java.util.List; -import java.util.UUID; +import java.util.*; @TestComponent public class UserFiller { - public List createRandomUsers(int amount) { - var list = new LinkedList(); + public Set createRandomUsers(int amount) { + var set = new HashSet(); for (int i = 0; i < amount; i++) { - list.add(new User(UserIdentifier.of(UUID.randomUUID().toString()))); + set.add(new User(UserIdentifier.of(UUID.randomUUID().toString()))); } - return list; + return set; } } From 894d882f9e155034eff6eb9108bc99c18a370430 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 16:08:32 +0700 Subject: [PATCH 06/13] Updated Vaadin files --- ...438174852fa051b44271a0a2847a0db8ff19645.js | 7 + ...f7cc0e32749668d262b0ca1e975d264e857a80d.js | 31 +++++ ...b8b3a433f64232e266b2048d170ff2d85d9542e.js | 16 +++ ...f00a96b95fd9e8bca2def1c68ee81d6438b887c.js | 32 +++++ ...9eca63bd44616fddc004af51e688c87f61700cf.js | 33 +++++ .../generated/flow/generated-flow-imports.js | 125 ++++++++---------- control-panel/frontend/generated/index.ts | 32 +++++ control-panel/frontend/generated/vaadin.ts | 2 - 8 files changed, 204 insertions(+), 74 deletions(-) create mode 100644 control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js create mode 100644 control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js create mode 100644 control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js create mode 100644 control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js create mode 100644 control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js create mode 100644 control-panel/frontend/generated/index.ts diff --git a/control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js b/control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js new file mode 100644 index 0000000..a2f40c5 --- /dev/null +++ b/control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js @@ -0,0 +1,7 @@ +import '@vaadin/polymer-legacy-adapter/style-modules.js'; +import '@vaadin/password-field/theme/lumo/vaadin-password-field.js'; +import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; +import 'Frontend/generated/jar-resources/buttonFunctions.js'; +import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; +import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js b/control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js new file mode 100644 index 0000000..473e46d --- /dev/null +++ b/control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js @@ -0,0 +1,31 @@ +import 'Frontend/generated/jar-resources/flow-component-renderer.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; +import '@vaadin/polymer-legacy-adapter/style-modules.js'; +import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; +import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; +import 'Frontend/generated/jar-resources/buttonFunctions.js'; +import 'Frontend/generated/jar-resources/dndConnector.js'; +import '@vaadin/details/theme/lumo/vaadin-details.js'; +import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; +import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; +import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; +import '@vaadin/icon/theme/lumo/vaadin-icon.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; +import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; +import 'Frontend/generated/jar-resources/contextMenuConnector.js'; +import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; +import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; +import 'Frontend/generated/jar-resources/gridConnector.js'; +import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; +import '@vaadin/icons/vaadin-iconset.js'; +import '@vaadin/text-area/theme/lumo/vaadin-text-area.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; +import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; +import 'Frontend/generated/jar-resources/lit-renderer.ts'; +import '@vaadin/notification/theme/lumo/vaadin-notification.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js b/control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js new file mode 100644 index 0000000..f5e1e86 --- /dev/null +++ b/control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js @@ -0,0 +1,16 @@ +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; +import '@vaadin/polymer-legacy-adapter/style-modules.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; +import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; +import '@vaadin/icon/theme/lumo/vaadin-icon.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; +import 'Frontend/generated/jar-resources/buttonFunctions.js'; +import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; +import '@vaadin/icons/vaadin-iconset.js'; +import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; +import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; +import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; +import '@vaadin/notification/theme/lumo/vaadin-notification.js'; +import 'Frontend/generated/jar-resources/flow-component-renderer.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js b/control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js new file mode 100644 index 0000000..a050329 --- /dev/null +++ b/control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js @@ -0,0 +1,32 @@ +import 'Frontend/generated/jar-resources/flow-component-renderer.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; +import '@vaadin/polymer-legacy-adapter/style-modules.js'; +import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; +import '@vaadin/radio-group/theme/lumo/vaadin-radio-group.js'; +import '@vaadin/radio-group/theme/lumo/vaadin-radio-button.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; +import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; +import 'Frontend/generated/jar-resources/buttonFunctions.js'; +import 'Frontend/generated/jar-resources/dndConnector.js'; +import '@vaadin/details/theme/lumo/vaadin-details.js'; +import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; +import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; +import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; +import '@vaadin/icon/theme/lumo/vaadin-icon.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; +import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; +import 'Frontend/generated/jar-resources/contextMenuConnector.js'; +import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; +import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; +import 'Frontend/generated/jar-resources/gridConnector.js'; +import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; +import '@vaadin/icons/vaadin-iconset.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; +import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; +import 'Frontend/generated/jar-resources/lit-renderer.ts'; +import '@vaadin/notification/theme/lumo/vaadin-notification.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js b/control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js new file mode 100644 index 0000000..69f718e --- /dev/null +++ b/control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js @@ -0,0 +1,33 @@ +import 'Frontend/generated/jar-resources/flow-component-renderer.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; +import '@vaadin/polymer-legacy-adapter/style-modules.js'; +import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; +import '@vaadin/radio-group/theme/lumo/vaadin-radio-group.js'; +import '@vaadin/radio-group/theme/lumo/vaadin-radio-button.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; +import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; +import 'Frontend/generated/jar-resources/buttonFunctions.js'; +import 'Frontend/generated/jar-resources/dndConnector.js'; +import '@vaadin/details/theme/lumo/vaadin-details.js'; +import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; +import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; +import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; +import '@vaadin/icon/theme/lumo/vaadin-icon.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; +import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; +import 'Frontend/generated/jar-resources/contextMenuConnector.js'; +import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; +import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; +import 'Frontend/generated/jar-resources/gridConnector.js'; +import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; +import '@vaadin/icons/vaadin-iconset.js'; +import '@vaadin/text-area/theme/lumo/vaadin-text-area.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; +import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; +import 'Frontend/generated/jar-resources/lit-renderer.ts'; +import '@vaadin/notification/theme/lumo/vaadin-notification.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/generated-flow-imports.js b/control-panel/frontend/generated/flow/generated-flow-imports.js index c2b32dd..dad102c 100644 --- a/control-panel/frontend/generated/flow/generated-flow-imports.js +++ b/control-panel/frontend/generated/flow/generated-flow-imports.js @@ -1,88 +1,69 @@ -import '@vaadin/common-frontend/ConnectionIndicator.js'; +import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import '@vaadin/accordion/src/vaadin-accordion.js'; -import '@vaadin/details/src/vaadin-details.js'; -import '@vaadin/accordion/src/vaadin-accordion-panel.js'; -import '@vaadin/app-layout/src/vaadin-app-layout.js'; -import '@vaadin/button/src/vaadin-button.js'; +import '@vaadin/icon/theme/lumo/vaadin-icon.js'; +import '@vaadin/icons/vaadin-iconset.js'; +import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; +import '@vaadin/button/theme/lumo/vaadin-button.js'; import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import '@vaadin/app-layout/src/vaadin-drawer-toggle.js'; -import '@vaadin/avatar/src/vaadin-avatar.js'; -import '@vaadin/avatar-group/src/vaadin-avatar-group.js'; -import '@vaadin/checkbox/src/vaadin-checkbox.js'; -import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js'; -import '@vaadin/combo-box/src/vaadin-combo-box.js'; +import '@vaadin/notification/theme/lumo/vaadin-notification.js'; import 'Frontend/generated/jar-resources/flow-component-renderer.js'; -import 'Frontend/generated/jar-resources/comboBoxConnector.js'; -import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js'; -import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js'; -import '@vaadin/context-menu/src/vaadin-context-menu.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; +import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; +import '@vaadin/radio-group/theme/lumo/vaadin-radio-group.js'; +import '@vaadin/radio-group/theme/lumo/vaadin-radio-button.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; +import 'Frontend/generated/jar-resources/dndConnector.js'; +import '@vaadin/details/theme/lumo/vaadin-details.js'; +import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; +import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; +import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; +import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; import 'Frontend/generated/jar-resources/contextMenuConnector.js'; import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; -import '@vaadin/custom-field/src/vaadin-custom-field.js'; -import '@vaadin/date-picker/src/vaadin-date-picker.js'; -import 'Frontend/generated/jar-resources/datepickerConnector.js'; -import '@vaadin/date-time-picker/src/vaadin-date-time-picker.js'; -import '@vaadin/time-picker/src/vaadin-time-picker.js'; -import 'Frontend/generated/jar-resources/vaadin-time-picker/timepickerConnector.js'; -import '@vaadin/dialog/src/vaadin-dialog.js'; -import 'Frontend/generated/jar-resources/dndConnector.js'; -import '@vaadin/field-highlighter/src/vaadin-field-highlighter.js'; -import '@vaadin/form-layout/src/vaadin-form-layout.js'; -import '@vaadin/form-layout/src/vaadin-form-item.js'; -import '@vaadin/grid/src/vaadin-grid-column-group.js'; -import '@vaadin/grid/src/vaadin-grid.js'; -import '@vaadin/grid/src/vaadin-grid-column.js'; -import '@vaadin/grid/src/vaadin-grid-sorter.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid.js'; +import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; +import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; import 'Frontend/generated/jar-resources/gridConnector.js'; -import '@vaadin/tooltip/src/vaadin-tooltip.js'; -import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; -import '@vaadin/icon/src/vaadin-icon.js'; -import '@vaadin/icons/vaadin-iconset.js'; -import '@vaadin/list-box/src/vaadin-list-box.js'; -import '@vaadin/item/src/vaadin-item.js'; -import '@vaadin/login/src/vaadin-login-form.js'; -import '@vaadin/login/src/vaadin-login-overlay.js'; -import 'Frontend/generated/jar-resources/menubarConnector.js'; -import '@vaadin/menu-bar/src/vaadin-menu-bar.js'; -import '@vaadin/message-input/src/vaadin-message-input.js'; -import 'Frontend/generated/jar-resources/messageListConnector.js'; -import '@vaadin/message-list/src/vaadin-message-list.js'; -import '@vaadin/notification/src/vaadin-notification.js'; -import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js'; -import '@vaadin/scroller/src/vaadin-scroller.js'; -import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js'; -import '@vaadin/progress-bar/src/vaadin-progress-bar.js'; -import '@vaadin/radio-group/src/vaadin-radio-button.js'; -import '@vaadin/radio-group/src/vaadin-radio-group.js'; -import '@vaadin/select/src/vaadin-select.js'; -import 'Frontend/generated/jar-resources/selectConnector.js'; -import 'Frontend/generated/jar-resources/tooltip.ts'; -import '@vaadin/side-nav/src/vaadin-side-nav.js'; -import '@vaadin/side-nav/src/vaadin-side-nav-item.js'; -import '@vaadin/split-layout/src/vaadin-split-layout.js'; -import '@vaadin/tabs/src/vaadin-tab.js'; -import '@vaadin/tabsheet/src/vaadin-tabsheet.js'; -import '@vaadin/tabs/src/vaadin-tabs.js'; -import 'Frontend/generated/jar-resources/vaadin-big-decimal-field.js'; -import '@vaadin/email-field/src/vaadin-email-field.js'; -import '@vaadin/integer-field/src/vaadin-integer-field.js'; -import '@vaadin/number-field/src/vaadin-number-field.js'; -import '@vaadin/password-field/src/vaadin-password-field.js'; -import '@vaadin/text-area/src/vaadin-text-area.js'; -import '@vaadin/text-field/src/vaadin-text-field.js'; +import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; +import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; +import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; import 'Frontend/generated/jar-resources/lit-renderer.ts'; -import '@vaadin/grid/src/vaadin-grid-tree-toggle.js'; -import '@vaadin/upload/src/vaadin-upload.js'; -import '@vaadin/virtual-list/src/vaadin-virtual-list.js'; -import 'Frontend/generated/jar-resources/virtualListConnector.js'; +import '@vaadin/common-frontend/ConnectionIndicator.js'; import '@vaadin/vaadin-lumo-styles/color-global.js'; import '@vaadin/vaadin-lumo-styles/typography-global.js'; import '@vaadin/vaadin-lumo-styles/sizing.js'; import '@vaadin/vaadin-lumo-styles/spacing.js'; import '@vaadin/vaadin-lumo-styles/style.js'; import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js'; -const loadOnDemand = (key) => { return Promise.resolve(0); } + +const loadOnDemand = (key) => { + const pending = []; + if (key === 'db3150d824231c9214a3c1d91184b7f02395caebe481a16a29c63841121179ba') { + pending.push(import('./chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js')); + } + if (key === 'ad3b749f8c176a86b14fa369887e22fef51afb8165384e5736c7046030b369fa') { + pending.push(import('./chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js')); + } + if (key === 'a7f0df9f1bf084521cb2f344b5b4a2bd632b352d0745369a572a88d4873662f3') { + pending.push(import('./chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js')); + } + if (key === '32e6740ceb6c3f1c7b07d32e31339c9e61b3608e4a25949d7e1225ce64b34968') { + pending.push(import('./chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js')); + } + if (key === '0953a3aba85bfd8d84ac16dfabaa1ff5b6a29348b23aa805384d5abc82efb666') { + pending.push(import('./chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js')); + } + if (key === '1f14de00b36692c96388e07da7b4d7d0aed00b531f10ad0a09b5ef8d7301712e') { + pending.push(import('./chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js')); + } + if (key === '2cf06ca82cdfddb866b229a647575c62452d9c0ba3e877154ca74b3a22d738d6') { + pending.push(import('./chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js')); + } + return Promise.all(pending); +} + window.Vaadin = window.Vaadin || {}; window.Vaadin.Flow = window.Vaadin.Flow || {}; window.Vaadin.Flow.loadOnDemand = loadOnDemand; \ No newline at end of file diff --git a/control-panel/frontend/generated/index.ts b/control-panel/frontend/generated/index.ts new file mode 100644 index 0000000..1b1823b --- /dev/null +++ b/control-panel/frontend/generated/index.ts @@ -0,0 +1,32 @@ +/****************************************************************************** + * This file is auto-generated by Vaadin. + * If you want to customize the entry point, you can copy this file or create + * your own `index.ts` in your frontend directory. + * By default, the `index.ts` file should be in `./frontend/` folder. + * + * NOTE: + * - You need to restart the dev-server after adding the new `index.ts` file. + * After that, all modifications to `index.ts` are recompiled automatically. + * - `index.js` is also supported if you don't want to use TypeScript. + ******************************************************************************/ + +// import Vaadin client-router to handle client-side and server-side navigation +import { Router } from '@vaadin/router'; + +// import Flow module to enable navigation to Vaadin server-side views +import { Flow } from 'Frontend/generated/jar-resources/Flow.js'; + +const { serverSideRoutes } = new Flow({ + imports: () => import('Frontend/generated/flow/generated-flow-imports.js') +}); + +const routes = [ + // for client-side, place routes below (more info https://hilla.dev/docs/lit/guides/routing#initializing-the-router) + + // for server-side, the next magic line sends all unmatched routes: + ...serverSideRoutes // IMPORTANT: this must be the last entry in the array +]; + +// Vaadin router needs an outlet in the index.html page to display views +const router = new Router(document.querySelector('#outlet')); +router.setRoutes(routes); diff --git a/control-panel/frontend/generated/vaadin.ts b/control-panel/frontend/generated/vaadin.ts index 464e371..aa27cb3 100644 --- a/control-panel/frontend/generated/vaadin.ts +++ b/control-panel/frontend/generated/vaadin.ts @@ -1,5 +1,3 @@ import './vaadin-featureflags.js'; import './index'; - -import 'Frontend/generated/jar-resources/vaadin-dev-tools/vaadin-dev-tools.js'; From fb1c7c3a118c23afc598227fe9244a29595bb9a8 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 20:35:09 +0700 Subject: [PATCH 07/13] Fixed all tests --- .../picker/repository/impl/container/NodeContainer.java | 1 + .../java/ru/dragonestia/picker/picker/LeastPickedTests.java | 4 +++- .../ru/dragonestia/picker/picker/SequentialFillingTests.java | 4 +++- settings.gradle | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java index e436b90..76716a2 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/NodeContainer.java @@ -95,6 +95,7 @@ public void removeRoomsByIds(@NotNull Collection roomIds) { public @NotNull Room pick(@NotNull Set users) { synchronized (picker) { var room = picker.pick(users); + room.addUsers(users, false); transactionListener.accept(new UserTransaction(room.getRoom(), users)); return room.getRoom(); } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java b/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java index 3256668..bbdbca6 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/LeastPickedTests.java @@ -41,12 +41,14 @@ public class LeastPickedTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { + var expectedRoomUsers = userRepository.usersOf(roomRepository.find(node, expectedRoomId).orElseThrow()).size(); + var room = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); var slots = room.getMaxSlots(); var users = userRepository.usersOf(room); Assertions.assertTrue(slots == -1 || slots >= users.size()); // check slots limitation - System.out.printf("Room(%s) has %s/%s users. Expected: %s, added: %s%n", room.getIdentifier(), users.size(), slots, expectedRoomId, usersAmount); + System.out.printf("Room(%s) has %s/%s users. Expected: %s(%s), added: %s%n", room.getIdentifier(), users.size(), slots, expectedRoomId, expectedRoomUsers, usersAmount); Assertions.assertEquals(expectedRoomId, room.getIdentifier()); } diff --git a/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java b/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java index 9ece42d..4dde327 100644 --- a/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java +++ b/server/src/test/java/ru/dragonestia/picker/picker/SequentialFillingTests.java @@ -41,12 +41,14 @@ public class SequentialFillingTests { @ParameterizedTest @ArgumentsSource(PickingArgumentProvider.class) void testPicking(String expectedRoomId, int usersAmount) { + var expectedRoomUsers = userRepository.usersOf(roomRepository.find(node, expectedRoomId).orElseThrow()).size(); + var room = roomRepository.pick(node, userFiller.createRandomUsers(usersAmount)); var slots = room.getMaxSlots(); var users = userRepository.usersOf(room); Assertions.assertTrue(slots == -1 || slots >= users.size()); // check slots limitation - System.out.printf("Room(%s) has %s/%s users. Expected: %s, added: %s%n", room.getIdentifier(), users.size(), slots, expectedRoomId, usersAmount); + System.out.printf("Room(%s) has %s/%s users. Expected: %s(%s), added: %s%n", room.getIdentifier(), users.size(), slots, expectedRoomId, expectedRoomUsers, usersAmount); Assertions.assertEquals(expectedRoomId, room.getIdentifier()); } diff --git a/settings.gradle b/settings.gradle index 0762c33..0c75b03 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,4 +4,5 @@ include 'server' include 'control-panel' include 'client-api' include 'client-impl' +include 'noiser' From 17e5341c35ee0dce7d21eff9386e8d2c1184449e Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 21:14:04 +0700 Subject: [PATCH 08/13] Fixed NullPointerException --- ...438174852fa051b44271a0a2847a0db8ff19645.js | 7 - ...f7cc0e32749668d262b0ca1e975d264e857a80d.js | 31 ----- ...b8b3a433f64232e266b2048d170ff2d85d9542e.js | 16 --- ...f00a96b95fd9e8bca2def1c68ee81d6438b887c.js | 32 ----- ...9eca63bd44616fddc004af51e688c87f61700cf.js | 33 ----- .../generated/flow/generated-flow-imports.js | 125 ++++++++++-------- control-panel/frontend/generated/vaadin.ts | 2 + .../picker/controller/RoomController.java | 2 +- 8 files changed, 75 insertions(+), 173 deletions(-) delete mode 100644 control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js delete mode 100644 control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js delete mode 100644 control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js delete mode 100644 control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js delete mode 100644 control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js diff --git a/control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js b/control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js deleted file mode 100644 index a2f40c5..0000000 --- a/control-panel/frontend/generated/flow/chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js +++ /dev/null @@ -1,7 +0,0 @@ -import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import '@vaadin/password-field/theme/lumo/vaadin-password-field.js'; -import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; -import '@vaadin/button/theme/lumo/vaadin-button.js'; -import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; -import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js b/control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js deleted file mode 100644 index 473e46d..0000000 --- a/control-panel/frontend/generated/flow/chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js +++ /dev/null @@ -1,31 +0,0 @@ -import 'Frontend/generated/jar-resources/flow-component-renderer.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; -import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; -import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; -import '@vaadin/button/theme/lumo/vaadin-button.js'; -import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import 'Frontend/generated/jar-resources/dndConnector.js'; -import '@vaadin/details/theme/lumo/vaadin-details.js'; -import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; -import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; -import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; -import '@vaadin/icon/theme/lumo/vaadin-icon.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; -import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; -import 'Frontend/generated/jar-resources/contextMenuConnector.js'; -import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; -import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; -import 'Frontend/generated/jar-resources/gridConnector.js'; -import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; -import '@vaadin/icons/vaadin-iconset.js'; -import '@vaadin/text-area/theme/lumo/vaadin-text-area.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; -import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; -import 'Frontend/generated/jar-resources/lit-renderer.ts'; -import '@vaadin/notification/theme/lumo/vaadin-notification.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js b/control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js deleted file mode 100644 index f5e1e86..0000000 --- a/control-panel/frontend/generated/flow/chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js +++ /dev/null @@ -1,16 +0,0 @@ -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; -import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; -import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; -import '@vaadin/icon/theme/lumo/vaadin-icon.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; -import '@vaadin/button/theme/lumo/vaadin-button.js'; -import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; -import '@vaadin/icons/vaadin-iconset.js'; -import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; -import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; -import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; -import '@vaadin/notification/theme/lumo/vaadin-notification.js'; -import 'Frontend/generated/jar-resources/flow-component-renderer.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js b/control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js deleted file mode 100644 index a050329..0000000 --- a/control-panel/frontend/generated/flow/chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js +++ /dev/null @@ -1,32 +0,0 @@ -import 'Frontend/generated/jar-resources/flow-component-renderer.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; -import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; -import '@vaadin/radio-group/theme/lumo/vaadin-radio-group.js'; -import '@vaadin/radio-group/theme/lumo/vaadin-radio-button.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; -import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; -import '@vaadin/button/theme/lumo/vaadin-button.js'; -import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import 'Frontend/generated/jar-resources/dndConnector.js'; -import '@vaadin/details/theme/lumo/vaadin-details.js'; -import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; -import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; -import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; -import '@vaadin/icon/theme/lumo/vaadin-icon.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; -import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; -import 'Frontend/generated/jar-resources/contextMenuConnector.js'; -import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; -import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; -import 'Frontend/generated/jar-resources/gridConnector.js'; -import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; -import '@vaadin/icons/vaadin-iconset.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; -import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; -import 'Frontend/generated/jar-resources/lit-renderer.ts'; -import '@vaadin/notification/theme/lumo/vaadin-notification.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js b/control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js deleted file mode 100644 index 69f718e..0000000 --- a/control-panel/frontend/generated/flow/chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js +++ /dev/null @@ -1,33 +0,0 @@ -import 'Frontend/generated/jar-resources/flow-component-renderer.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; -import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; -import '@vaadin/radio-group/theme/lumo/vaadin-radio-group.js'; -import '@vaadin/radio-group/theme/lumo/vaadin-radio-button.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; -import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; -import '@vaadin/button/theme/lumo/vaadin-button.js'; -import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import 'Frontend/generated/jar-resources/dndConnector.js'; -import '@vaadin/details/theme/lumo/vaadin-details.js'; -import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; -import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; -import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; -import '@vaadin/icon/theme/lumo/vaadin-icon.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; -import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; -import 'Frontend/generated/jar-resources/contextMenuConnector.js'; -import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; -import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; -import 'Frontend/generated/jar-resources/gridConnector.js'; -import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; -import '@vaadin/icons/vaadin-iconset.js'; -import '@vaadin/text-area/theme/lumo/vaadin-text-area.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; -import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; -import 'Frontend/generated/jar-resources/lit-renderer.ts'; -import '@vaadin/notification/theme/lumo/vaadin-notification.js'; \ No newline at end of file diff --git a/control-panel/frontend/generated/flow/generated-flow-imports.js b/control-panel/frontend/generated/flow/generated-flow-imports.js index dad102c..c2b32dd 100644 --- a/control-panel/frontend/generated/flow/generated-flow-imports.js +++ b/control-panel/frontend/generated/flow/generated-flow-imports.js @@ -1,69 +1,88 @@ -import '@vaadin/tooltip/theme/lumo/vaadin-tooltip.js'; +import '@vaadin/common-frontend/ConnectionIndicator.js'; import '@vaadin/polymer-legacy-adapter/style-modules.js'; -import '@vaadin/icon/theme/lumo/vaadin-icon.js'; -import '@vaadin/icons/vaadin-iconset.js'; -import '@vaadin/horizontal-layout/theme/lumo/vaadin-horizontal-layout.js'; -import '@vaadin/button/theme/lumo/vaadin-button.js'; +import '@vaadin/accordion/src/vaadin-accordion.js'; +import '@vaadin/details/src/vaadin-details.js'; +import '@vaadin/accordion/src/vaadin-accordion-panel.js'; +import '@vaadin/app-layout/src/vaadin-app-layout.js'; +import '@vaadin/button/src/vaadin-button.js'; import 'Frontend/generated/jar-resources/buttonFunctions.js'; -import '@vaadin/notification/theme/lumo/vaadin-notification.js'; +import '@vaadin/app-layout/src/vaadin-drawer-toggle.js'; +import '@vaadin/avatar/src/vaadin-avatar.js'; +import '@vaadin/avatar-group/src/vaadin-avatar-group.js'; +import '@vaadin/checkbox/src/vaadin-checkbox.js'; +import '@vaadin/checkbox-group/src/vaadin-checkbox-group.js'; +import '@vaadin/combo-box/src/vaadin-combo-box.js'; import 'Frontend/generated/jar-resources/flow-component-renderer.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav.js'; -import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column.js'; -import '@vaadin/radio-group/theme/lumo/vaadin-radio-group.js'; -import '@vaadin/radio-group/theme/lumo/vaadin-radio-button.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-app-layout.js'; -import 'Frontend/generated/jar-resources/dndConnector.js'; -import '@vaadin/details/theme/lumo/vaadin-details.js'; -import '@vaadin/dialog/theme/lumo/vaadin-dialog.js'; -import '@vaadin/vertical-layout/theme/lumo/vaadin-vertical-layout.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-column-group.js'; -import '@vaadin/side-nav/theme/lumo/vaadin-side-nav-item.js'; -import '@vaadin/context-menu/theme/lumo/vaadin-context-menu.js'; +import 'Frontend/generated/jar-resources/comboBoxConnector.js'; +import '@vaadin/multi-select-combo-box/src/vaadin-multi-select-combo-box.js'; +import '@vaadin/confirm-dialog/src/vaadin-confirm-dialog.js'; +import '@vaadin/context-menu/src/vaadin-context-menu.js'; import 'Frontend/generated/jar-resources/contextMenuConnector.js'; import 'Frontend/generated/jar-resources/contextMenuTargetConnector.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid.js'; -import '@vaadin/grid/theme/lumo/vaadin-grid-sorter.js'; -import '@vaadin/checkbox/theme/lumo/vaadin-checkbox.js'; +import '@vaadin/custom-field/src/vaadin-custom-field.js'; +import '@vaadin/date-picker/src/vaadin-date-picker.js'; +import 'Frontend/generated/jar-resources/datepickerConnector.js'; +import '@vaadin/date-time-picker/src/vaadin-date-time-picker.js'; +import '@vaadin/time-picker/src/vaadin-time-picker.js'; +import 'Frontend/generated/jar-resources/vaadin-time-picker/timepickerConnector.js'; +import '@vaadin/dialog/src/vaadin-dialog.js'; +import 'Frontend/generated/jar-resources/dndConnector.js'; +import '@vaadin/field-highlighter/src/vaadin-field-highlighter.js'; +import '@vaadin/form-layout/src/vaadin-form-layout.js'; +import '@vaadin/form-layout/src/vaadin-form-item.js'; +import '@vaadin/grid/src/vaadin-grid-column-group.js'; +import '@vaadin/grid/src/vaadin-grid.js'; +import '@vaadin/grid/src/vaadin-grid-column.js'; +import '@vaadin/grid/src/vaadin-grid-sorter.js'; import 'Frontend/generated/jar-resources/gridConnector.js'; -import '@vaadin/text-field/theme/lumo/vaadin-text-field.js'; -import '@vaadin/app-layout/theme/lumo/vaadin-drawer-toggle.js'; -import '@vaadin/scroller/theme/lumo/vaadin-scroller.js'; +import '@vaadin/tooltip/src/vaadin-tooltip.js'; +import 'Frontend/generated/jar-resources/vaadin-grid-flow-selection-column.js'; +import '@vaadin/icon/src/vaadin-icon.js'; +import '@vaadin/icons/vaadin-iconset.js'; +import '@vaadin/list-box/src/vaadin-list-box.js'; +import '@vaadin/item/src/vaadin-item.js'; +import '@vaadin/login/src/vaadin-login-form.js'; +import '@vaadin/login/src/vaadin-login-overlay.js'; +import 'Frontend/generated/jar-resources/menubarConnector.js'; +import '@vaadin/menu-bar/src/vaadin-menu-bar.js'; +import '@vaadin/message-input/src/vaadin-message-input.js'; +import 'Frontend/generated/jar-resources/messageListConnector.js'; +import '@vaadin/message-list/src/vaadin-message-list.js'; +import '@vaadin/notification/src/vaadin-notification.js'; +import '@vaadin/horizontal-layout/src/vaadin-horizontal-layout.js'; +import '@vaadin/scroller/src/vaadin-scroller.js'; +import '@vaadin/vertical-layout/src/vaadin-vertical-layout.js'; +import '@vaadin/progress-bar/src/vaadin-progress-bar.js'; +import '@vaadin/radio-group/src/vaadin-radio-button.js'; +import '@vaadin/radio-group/src/vaadin-radio-group.js'; +import '@vaadin/select/src/vaadin-select.js'; +import 'Frontend/generated/jar-resources/selectConnector.js'; +import 'Frontend/generated/jar-resources/tooltip.ts'; +import '@vaadin/side-nav/src/vaadin-side-nav.js'; +import '@vaadin/side-nav/src/vaadin-side-nav-item.js'; +import '@vaadin/split-layout/src/vaadin-split-layout.js'; +import '@vaadin/tabs/src/vaadin-tab.js'; +import '@vaadin/tabsheet/src/vaadin-tabsheet.js'; +import '@vaadin/tabs/src/vaadin-tabs.js'; +import 'Frontend/generated/jar-resources/vaadin-big-decimal-field.js'; +import '@vaadin/email-field/src/vaadin-email-field.js'; +import '@vaadin/integer-field/src/vaadin-integer-field.js'; +import '@vaadin/number-field/src/vaadin-number-field.js'; +import '@vaadin/password-field/src/vaadin-password-field.js'; +import '@vaadin/text-area/src/vaadin-text-area.js'; +import '@vaadin/text-field/src/vaadin-text-field.js'; import 'Frontend/generated/jar-resources/lit-renderer.ts'; -import '@vaadin/common-frontend/ConnectionIndicator.js'; +import '@vaadin/grid/src/vaadin-grid-tree-toggle.js'; +import '@vaadin/upload/src/vaadin-upload.js'; +import '@vaadin/virtual-list/src/vaadin-virtual-list.js'; +import 'Frontend/generated/jar-resources/virtualListConnector.js'; import '@vaadin/vaadin-lumo-styles/color-global.js'; import '@vaadin/vaadin-lumo-styles/typography-global.js'; import '@vaadin/vaadin-lumo-styles/sizing.js'; import '@vaadin/vaadin-lumo-styles/spacing.js'; import '@vaadin/vaadin-lumo-styles/style.js'; import '@vaadin/vaadin-lumo-styles/vaadin-iconset.js'; - -const loadOnDemand = (key) => { - const pending = []; - if (key === 'db3150d824231c9214a3c1d91184b7f02395caebe481a16a29c63841121179ba') { - pending.push(import('./chunks/chunk-a8dc2de6451e2fb9ada5149099eca63bd44616fddc004af51e688c87f61700cf.js')); - } - if (key === 'ad3b749f8c176a86b14fa369887e22fef51afb8165384e5736c7046030b369fa') { - pending.push(import('./chunks/chunk-0c6ec94efe36378e73adb47fc438174852fa051b44271a0a2847a0db8ff19645.js')); - } - if (key === 'a7f0df9f1bf084521cb2f344b5b4a2bd632b352d0745369a572a88d4873662f3') { - pending.push(import('./chunks/chunk-442b7bb85666e65256bd58cc7f7cc0e32749668d262b0ca1e975d264e857a80d.js')); - } - if (key === '32e6740ceb6c3f1c7b07d32e31339c9e61b3608e4a25949d7e1225ce64b34968') { - pending.push(import('./chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js')); - } - if (key === '0953a3aba85bfd8d84ac16dfabaa1ff5b6a29348b23aa805384d5abc82efb666') { - pending.push(import('./chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js')); - } - if (key === '1f14de00b36692c96388e07da7b4d7d0aed00b531f10ad0a09b5ef8d7301712e') { - pending.push(import('./chunks/chunk-8d8b0bd7d7a80985493227f01f00a96b95fd9e8bca2def1c68ee81d6438b887c.js')); - } - if (key === '2cf06ca82cdfddb866b229a647575c62452d9c0ba3e877154ca74b3a22d738d6') { - pending.push(import('./chunks/chunk-81110d45df7602896d2ac4725b8b3a433f64232e266b2048d170ff2d85d9542e.js')); - } - return Promise.all(pending); -} - +const loadOnDemand = (key) => { return Promise.resolve(0); } window.Vaadin = window.Vaadin || {}; window.Vaadin.Flow = window.Vaadin.Flow || {}; window.Vaadin.Flow.loadOnDemand = loadOnDemand; \ No newline at end of file diff --git a/control-panel/frontend/generated/vaadin.ts b/control-panel/frontend/generated/vaadin.ts index aa27cb3..464e371 100644 --- a/control-panel/frontend/generated/vaadin.ts +++ b/control-panel/frontend/generated/vaadin.ts @@ -1,3 +1,5 @@ import './vaadin-featureflags.js'; import './index'; + +import 'Frontend/generated/jar-resources/vaadin-dev-tools/vaadin-dev-tools.js'; diff --git a/server/src/main/java/ru/dragonestia/picker/controller/RoomController.java b/server/src/main/java/ru/dragonestia/picker/controller/RoomController.java index 2c1a578..9eda134 100644 --- a/server/src/main/java/ru/dragonestia/picker/controller/RoomController.java +++ b/server/src/main/java/ru/dragonestia/picker/controller/RoomController.java @@ -57,8 +57,8 @@ ResponseEntity register( ) { var node = nodeService.find(nodeId).orElseThrow(() -> new NodeNotFoundException(nodeId)); var room = roomFactory.create(RoomIdentifier.of(roomId), node, slots, payload, persist); - room.setLocked(locked); roomService.create(room); + room.setLocked(locked); return ResponseEntity.ok().build(); } From a1bbeba73b35691bd6709a7a014cd9c756fcc887 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 22:16:57 +0700 Subject: [PATCH 09/13] Used fair locks --- .../picker/repository/impl/container/RoomContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java index bf7d60f..6becbf4 100644 --- a/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java +++ b/server/src/main/java/ru/dragonestia/picker/repository/impl/container/RoomContainer.java @@ -20,7 +20,7 @@ public class RoomContainer { private final Room room; private final NodeContainer container; - private final ReadWriteLock usersLock = new ReentrantReadWriteLock(); + private final ReadWriteLock usersLock = new ReentrantReadWriteLock(true); private final Set users = new HashSet<>(); public RoomContainer(@NotNull Room room, @NotNull NodeContainer container) { From 15bb3c639b0d06178f57662bcf1295f69f1f5460 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 22:18:43 +0700 Subject: [PATCH 10/13] Changes timings for deletion users in stress test --- noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java b/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java index 256172f..75eba8d 100644 --- a/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java +++ b/noiser/src/main/java/ru/dragonestia/picker/noiser/Main.java @@ -64,7 +64,7 @@ private void initRooms() { for (var nodeId: nodes) { for (int i = 0; i < roomsPerNode; i++) { client.getRoomRepository().saveRoom( - new RoomDefinition(nodeId, RoomIdentifier.of(UUID.randomUUID().toString())).setMaxSlots(roomsPerNode) + new RoomDefinition(nodeId, RoomIdentifier.of(UUID.randomUUID().toString())).setMaxSlots(10) ); } } @@ -104,7 +104,7 @@ private void pickUsers() { } catch (Exception ex) { System.out.println("Error(" + ex.getClass().getSimpleName() + "): " + ex.getMessage()); } - }, random.nextInt(1000) + 100, TimeUnit.MILLISECONDS); + }, random.nextInt(10) + 1, TimeUnit.SECONDS); } } catch (Exception ex) { System.out.println("Error(" + ex.getClass().getSimpleName() + "): " + ex.getMessage()); From 79dc40492bf8888d8b5979725d02da11366f67b1 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 23:11:31 +0700 Subject: [PATCH 11/13] Fixed metrics --- .../picker/aspect/UserMetricsAspect.java | 53 ++++++------------- .../ru/dragonestia/picker/model/Room.java | 8 +-- .../picker/model/factory/RoomFactory.java | 4 +- 3 files changed, 18 insertions(+), 47 deletions(-) diff --git a/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java b/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java index ee0003d..db9fd7c 100644 --- a/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java +++ b/server/src/main/java/ru/dragonestia/picker/aspect/UserMetricsAspect.java @@ -9,19 +9,16 @@ import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; -import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; -import ru.dragonestia.picker.event.UpdateRoomLockStateEvent; import ru.dragonestia.picker.model.Node; import ru.dragonestia.picker.model.Room; import ru.dragonestia.picker.repository.RoomRepository; import ru.dragonestia.picker.repository.UserRepository; +import ru.dragonestia.picker.repository.impl.ContainerRepository; -import java.util.HashSet; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -31,6 +28,7 @@ @Log4j2 public class UserMetricsAspect { + private final ContainerRepository containerRepository; private final RoomRepository roomRepository; private final UserRepository userRepository; private final MeterRegistry meterRegistry; @@ -53,33 +51,8 @@ void onUnlinkUsers(Room room) { countAllUsers(room); } - @AfterReturning(value = "execution(void ru.dragonestia.picker.repository.RoomRepository.create(ru.dragonestia.picker.model.Room)) && args(room)", argNames = "room") - void onCreateRoom(Room room) { - checkRoom(room); - } - - @After(value = "execution(void ru.dragonestia.picker.repository.UserRepository.onRemoveRoom(ru.dragonestia.picker.model.Room)) && args(room, ..)", argNames = "room") - void onRemoveRoom(Room room) { - countAllUsers(room); - } - private void countAllUsers(Room room) { totalUsers.set(userRepository.countAllUsers()); - - checkRoom(room); - } - - private void checkRoom(Room room) { - var set = data.get(room.getNodeIdentifier()).locked(); - if (room.isLocked()) { - set.add(room); - return; - } - if (!room.hasUnlimitedSlots() && userRepository.usersOf(room).size() >= room.getMaxSlots()) { - set.add(room); - return; - } - set.remove(room); } @After(value = "execution(void ru.dragonestia.picker.repository.NodeRepository.create(ru.dragonestia.picker.model.Node)) && args(node)", argNames = "node") @@ -94,7 +67,7 @@ void onCreateNode(Node node) { .baseUnit("1s") .register(meterRegistry); - var lockedGauge = Gauge.builder("roompicker_locked_rooms", () -> data.get(nodeId).locked().size()) + var lockedGauge = Gauge.builder("roompicker_locked_rooms", () -> data.get(nodeId).locked()) .tag("nodeId", nodeId) .register(meterRegistry); @@ -102,7 +75,7 @@ void onCreateNode(Node node) { .tag("nodeId", nodeId) .register(meterRegistry); - data.put(nodeId, new NodeData(gauge, new AtomicInteger(0), counter, new HashSet<>(), lockedGauge, roomsGauge)); + data.put(nodeId, new NodeData(gauge, new AtomicInteger(0), counter, new AtomicInteger(0), lockedGauge, roomsGauge)); } @After(value = "execution(* ru.dragonestia.picker.repository.NodeRepository.delete(ru.dragonestia.picker.model.Node)) && args(node)", argNames = "node") @@ -120,17 +93,23 @@ void onPickRoom(Node node) { data.get(node.getIdentifier()).picksPerMinute().increment(); } - @EventListener - void onRoomChangeLockState(UpdateRoomLockStateEvent event) { - checkRoom(event.room()); - } - @Scheduled(fixedDelay = 3_000) void updateUserMetrics() { userRepository.countUsersForNodes().forEach((nodeId, users) -> { Optional.ofNullable(data.get(nodeId)).ifPresent(node -> node.users().set(users)); }); + + containerRepository.all().forEach(nodeContainer -> { + var locked = data.get(nodeContainer.getNode().getIdentifier()).locked(); + locked.set(0); + + nodeContainer.allRooms().forEach(roomContainer -> { + if (roomContainer.canBePicked(1)) return; + + locked.incrementAndGet(); + }); + }); } - private record NodeData(Gauge usersGauge, AtomicInteger users, Counter picksPerMinute, Set locked, Gauge lockedGauge, Gauge roomsGauge) {} + private record NodeData(Gauge usersGauge, AtomicInteger users, Counter picksPerMinute, AtomicInteger locked, Gauge lockedGauge, Gauge roomsGauge) {} } diff --git a/server/src/main/java/ru/dragonestia/picker/model/Room.java b/server/src/main/java/ru/dragonestia/picker/model/Room.java index fb3bfa5..5404fc5 100644 --- a/server/src/main/java/ru/dragonestia/picker/model/Room.java +++ b/server/src/main/java/ru/dragonestia/picker/model/Room.java @@ -19,15 +19,13 @@ public class Room implements IRoom { private final String payload; private final boolean persist; private boolean locked = false; - private final UpdateRoomLockStateEvent.Listener updateLockStateListener; - public Room(@NotNull RoomIdentifier identifier, @NotNull Node node, int slots, @NotNull String payload, boolean persist, @Nullable UpdateRoomLockStateEvent.Listener listener) { + public Room(@NotNull RoomIdentifier identifier, @NotNull Node node, int slots, @NotNull String payload, boolean persist) { this.identifier = identifier.getValue(); this.nodeIdentifier = node.getIdentifier(); this.slots = slots; this.payload = payload; this.persist = persist; - this.updateLockStateListener = listener; } @Override @@ -52,10 +50,6 @@ public boolean isLocked() { public void setLocked(boolean value) { locked = value; - - if (updateLockStateListener != null) { - updateLockStateListener.accept(new UpdateRoomLockStateEvent(this)); - } } @Override diff --git a/server/src/main/java/ru/dragonestia/picker/model/factory/RoomFactory.java b/server/src/main/java/ru/dragonestia/picker/model/factory/RoomFactory.java index 938e6c2..7fee26e 100644 --- a/server/src/main/java/ru/dragonestia/picker/model/factory/RoomFactory.java +++ b/server/src/main/java/ru/dragonestia/picker/model/factory/RoomFactory.java @@ -13,10 +13,8 @@ @RequiredArgsConstructor public class RoomFactory { - private final ApplicationEventPublisher eventPublisher; - @Contract("_, _, _, _, _ -> new") public @NotNull Room create(@NotNull RoomIdentifier identifier, @NotNull Node node, int slots, @NotNull String payload, boolean persist) { - return new Room(identifier, node, slots, payload, persist, eventPublisher::publishEvent); + return new Room(identifier, node, slots, payload, persist); } } From 02d59a3dfa1da39dabffa02bbf12492e7b15389e Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 23:12:01 +0700 Subject: [PATCH 12/13] Updated Grafana dashboard --- RoomPicker-Grafana-Dashboard.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/RoomPicker-Grafana-Dashboard.json b/RoomPicker-Grafana-Dashboard.json index 8225074..e2abb7c 100644 --- a/RoomPicker-Grafana-Dashboard.json +++ b/RoomPicker-Grafana-Dashboard.json @@ -453,7 +453,7 @@ "axisPlacement": "left", "barAlignment": 0, "drawStyle": "bars", - "fillOpacity": 30, + "fillOpacity": 26, "gradientMode": "none", "hideFrom": { "legend": false, @@ -462,7 +462,7 @@ }, "insertNulls": false, "lineInterpolation": "stepAfter", - "lineWidth": 2, + "lineWidth": 3, "pointSize": 3, "scaleDistribution": { "type": "linear" @@ -471,7 +471,7 @@ "spanNulls": false, "stacking": { "group": "A", - "mode": "normal" + "mode": "none" }, "thresholdsStyle": { "mode": "off" @@ -725,13 +725,13 @@ "list": [] }, "time": { - "from": "now-6h", + "from": "now-15m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "RoomPicker", "uid": "e3009deb-a342-4a32-9d13-3b44ed3461bc", - "version": 24, + "version": 26, "weekStart": "" } \ No newline at end of file From 10f362d7cf431f31a6145fada841bef1538e2715 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Sat, 16 Mar 2024 23:26:25 +0700 Subject: [PATCH 13/13] Updated README.md --- .github/img/grafana.png | Bin 0 -> 200065 bytes README.md | 11 ++++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .github/img/grafana.png diff --git a/.github/img/grafana.png b/.github/img/grafana.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b4f8c4fd796f2f0532bdf782c8c277cc016a1a GIT binary patch literal 200065 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index 0f9e075..ddfbb91 100644 --- a/README.md +++ b/README.md @@ -59,4 +59,13 @@ class Example { } } } -``` \ No newline at end of file +``` + + +Stress testing +---------- + +If you want to stress test the server, you can run the script from the `noiser` module. +Before starting stress testing, you'd better run Prometheus and Grafana from `docker-compose.yml`. +In Grafana, you can see this view: +![Grafana](.github/img/grafana.png) \ No newline at end of file