* Moved method `ChatRoomFactory.createChatRoom(UUID, String)` to `ChatHome`.
* Allowed `null`-values for `ChatRoom.shard`.
* Moved logic from `InMemoryChatHomeService` into `SimpleChatHome` respective
`ShardedChatHome` and removed obsolete class.
* Adapted the configuration of the tests to the model changes:
import de.juplo.kafka.chat.backend.domain.ChatHome;
import de.juplo.kafka.chat.backend.domain.ChatRoom;
-import de.juplo.kafka.chat.backend.domain.ChatRoomFactory;
import de.juplo.kafka.chat.backend.persistence.StorageStrategy;
import lombok.RequiredArgsConstructor;
import org.springframework.http.codec.ServerSentEvent;
public class ChatBackendController
{
private final ChatHome chatHome;
- private final ChatRoomFactory factory;
private final StorageStrategy storageStrategy;
public Mono<ChatRoomInfoTo> create(@RequestBody String name)
{
UUID chatRoomId = UUID.randomUUID();
- return factory
+ return chatHome
.createChatRoom(chatRoomId, name)
.map(ChatRoomInfoTo::from);
}
{
private UUID id;
private String name;
- private int shard;
+ private Integer shard;
public static ChatRoomInfoTo from(ChatRoomInfo info)
public interface ChatHome
{
+ Mono<ChatRoomInfo> createChatRoom(UUID id, String name);
+
Mono<ChatRoom> getChatRoom(UUID id);
Flux<ChatRoom> getChatRooms();
public ChatRoom(
UUID id,
String name,
- int shard,
+ Integer shard,
Clock clock,
ChatRoomService service,
int bufferSize)
+++ /dev/null
-package de.juplo.kafka.chat.backend.domain;
-
-import reactor.core.publisher.Mono;
-
-import java.util.UUID;
-
-
-public interface ChatRoomFactory
-{
- Mono<ChatRoomInfo> createChatRoom(UUID id, String name);
-}
@Getter
private final String name;
@Getter
- private final int shard;
+ private final Integer shard;
}
+++ /dev/null
-package de.juplo.kafka.chat.backend.persistence.inmemory;
-
-import de.juplo.kafka.chat.backend.domain.ChatRoom;
-import lombok.extern.slf4j.Slf4j;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-import java.util.*;
-import java.util.stream.IntStream;
-
-
-@Slf4j
-public class InMemoryChatHomeService
-{
- private final Map<UUID, ChatRoom>[] chatrooms;
-
-
- public InMemoryChatHomeService(
- int numShards,
- int[] ownedShards,
- Flux<ChatRoom> chatroomFlux)
- {
- log.debug("Creating InMemoryChatHomeService");
- this.chatrooms = new Map[numShards];
- Set<Integer> owned = Arrays
- .stream(ownedShards)
- .collect(
- () -> new HashSet<>(),
- (set, i) -> set.add(i),
- (a, b) -> a.addAll(b));
- for (int shard = 0; shard < numShards; shard++)
- {
- chatrooms[shard] = owned.contains(shard)
- ? new HashMap<>()
- : null;
- }
- chatroomFlux
- .filter(chatRoom ->
- {
- if (owned.contains(chatRoom.getShard()))
- {
- return true;
- }
- else
- {
- log.info("Ignoring not owned chat-room {}", chatRoom);
- return false;
- }
- })
- .toStream()
- .forEach(chatroom -> chatrooms[chatroom.getShard()].put(chatroom.getId(), chatroom));
- }
-
- public void putChatRoom(ChatRoom chatRoom)
- {
- chatrooms[chatRoom.getShard()].put(chatRoom.getId(), chatRoom);
- }
-
- public Mono<ChatRoom> getChatRoom(int shard, UUID id)
- {
- return Mono.justOrEmpty(chatrooms[shard].get(id));
- }
-
- public int[] getOwnedShards()
- {
- return IntStream
- .range(0, chatrooms.length)
- .filter(i -> chatrooms[i] != null)
- .toArray();
- }
-
- public Flux<ChatRoom> getChatRooms(int shard)
- {
- return Flux.fromStream(chatrooms[shard].values().stream());
- }
-}
+++ /dev/null
-package de.juplo.kafka.chat.backend.persistence.inmemory;
-
-import de.juplo.kafka.chat.backend.domain.*;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-import java.time.Clock;
-import java.util.UUID;
-
-
-@RequiredArgsConstructor
-@Slf4j
-public class InMemoryChatRoomFactory implements ChatRoomFactory
-{
- private final InMemoryChatHomeService chatHomeService;
- private final ShardingStrategy shardingStrategy;
- private final Clock clock;
- private final int bufferSize;
-
-
- @Override
- public Mono<ChatRoomInfo> createChatRoom(UUID id, String name)
- {
- log.info("Creating ChatRoom with buffer-size {}", bufferSize);
- int shard = shardingStrategy.selectShard(id);
- ChatRoomService service = new InMemoryChatRoomService(Flux.empty());
- ChatRoom chatRoom = new ChatRoom(id, name, shard, clock, service, bufferSize);
- chatHomeService.putChatRoom(chatRoom);
- return Mono.just(chatRoom);
- }
-}
package de.juplo.kafka.chat.backend.persistence.inmemory;
import de.juplo.kafka.chat.backend.ChatBackendProperties;
-import de.juplo.kafka.chat.backend.ChatBackendProperties.ShardingStrategyType;
import de.juplo.kafka.chat.backend.domain.ChatHome;
import de.juplo.kafka.chat.backend.persistence.StorageStrategy;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
name = "sharding-strategy",
havingValue = "none",
matchIfMissing = true)
- ChatHome noneShardingChatHome(InMemoryChatHomeService chatHomeService)
+ ChatHome noneShardingChatHome(
+ ChatBackendProperties properties,
+ StorageStrategy storageStrategy,
+ Clock clock)
{
- return new SimpleChatHome(chatHomeService);
+ return new SimpleChatHome(
+ storageStrategy.read(),
+ clock,
+ properties.getChatroomBufferSize());
}
@Bean
havingValue = "kafkalike")
ChatHome kafkalikeShardingChatHome(
ChatBackendProperties properties,
- InMemoryChatHomeService chatHomeService)
+ StorageStrategy storageStrategy,
+ Clock clock)
{
int numShards = properties.getInmemory().getNumShards();
SimpleChatHome[] chatHomes = new SimpleChatHome[numShards];
IntStream
.of(properties.getInmemory().getOwnedShards())
- .forEach(shard -> chatHomes[shard] = new SimpleChatHome(chatHomeService, shard));
+ .forEach(shard -> chatHomes[shard] = new SimpleChatHome(
+ shard,
+ storageStrategy.read(),
+ clock,
+ properties.getChatroomBufferSize()));
ShardingStrategy strategy = new KafkaLikeShardingStrategy(numShards);
return new ShardedChatHome(chatHomes, strategy);
}
- @Bean
- InMemoryChatHomeService chatHomeService(
- ChatBackendProperties properties,
- StorageStrategy storageStrategy)
- {
- ShardingStrategyType sharding =
- properties.getInmemory().getShardingStrategy();
- int numShards = sharding == ShardingStrategyType.none
- ? 1
- : properties.getInmemory().getNumShards();
- int[] ownedShards = sharding == ShardingStrategyType.none
- ? new int[] { 0 }
- : properties.getInmemory().getOwnedShards();
- return new InMemoryChatHomeService(
- numShards,
- ownedShards,
- storageStrategy.read());
- }
-
- @Bean
- InMemoryChatRoomFactory chatRoomFactory(
- InMemoryChatHomeService service,
- ShardingStrategy strategy,
- Clock clock,
- ChatBackendProperties properties)
- {
- return new InMemoryChatRoomFactory(
- service,
- strategy,
- clock,
- properties.getChatroomBufferSize());
- }
-
@ConditionalOnProperty(
prefix = "chat.backend.inmemory",
name = "sharding-strategy",
package de.juplo.kafka.chat.backend.persistence.inmemory;
-import de.juplo.kafka.chat.backend.domain.ChatHome;
-import de.juplo.kafka.chat.backend.domain.ChatRoom;
-import de.juplo.kafka.chat.backend.domain.ShardNotOwnedException;
+import de.juplo.kafka.chat.backend.domain.*;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
}
+ @Override
+ public Mono<ChatRoomInfo> createChatRoom(UUID id, String name)
+ {
+ int shard = shardingStrategy.selectShard(id);
+ return chatHomes[shard] == null
+ ? Mono.error(new ShardNotOwnedException(shard))
+ : chatHomes[shard].createChatRoom(id, name);
+ }
+
@Override
public Mono<ChatRoom> getChatRoom(UUID id)
{
int shard = selectShard(id);
return chatHomes[shard] == null
? Mono.error(new ShardNotOwnedException(shard))
- : chatHomes[shard].getChatRoom(id);
+ : chatHomes[shard]
+ .getChatRoom(id)
+ .onErrorMap(throwable -> throwable instanceof UnknownChatroomException
+ ? new UnknownChatroomException(
+ id,
+ shard,
+ ownedShards.stream().mapToInt(i -> i.intValue()).toArray())
+ : throwable);
}
@Override
package de.juplo.kafka.chat.backend.persistence.inmemory;
-import de.juplo.kafka.chat.backend.domain.ChatHome;
-import de.juplo.kafka.chat.backend.domain.ChatRoom;
-import de.juplo.kafka.chat.backend.domain.UnknownChatroomException;
+import de.juplo.kafka.chat.backend.domain.*;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import java.time.Clock;
import java.util.*;
@Slf4j
public class SimpleChatHome implements ChatHome
{
- private final InMemoryChatHomeService service;
- private final int shard;
+ private final Integer shard;
+ private final Map<UUID, ChatRoom> chatRooms;
+ private final Clock clock;
+ private final int bufferSize;
- public SimpleChatHome(InMemoryChatHomeService service, int shard)
+
+ public SimpleChatHome(
+ Flux<ChatRoom> chatroomFlux,
+ Clock clock,
+ int bufferSize)
+ {
+ this(null, chatroomFlux, clock, bufferSize);
+ }
+
+ public SimpleChatHome(
+ Integer shard,
+ Flux<ChatRoom> chatroomFlux,
+ Clock clock,
+ int bufferSize)
{
log.info("Created SimpleChatHome for shard {}", shard);
- this.service = service;
+;
this.shard = shard;
+ this.chatRooms = new HashMap<>();
+ chatroomFlux
+ .filter(chatRoom ->
+ {
+ if (shard == null || chatRoom.getShard() == shard)
+ {
+ return true;
+ }
+ else
+ {
+ log.info(
+ "SimpleChatHome for shard {} ignores not owned chat-room {}",
+ shard,
+ chatRoom);
+ return false;
+ }
+ })
+ .toStream()
+ .forEach(chatroom -> chatRooms.put(chatroom.getId(), chatroom));
+ this.clock = clock;
+ this.bufferSize = bufferSize;
}
- public SimpleChatHome(InMemoryChatHomeService service)
+
+ @Override
+ public Mono<ChatRoomInfo> createChatRoom(UUID id, String name)
{
- this(service, 0);
+ log.info("Creating ChatRoom with buffer-size {}", bufferSize);
+ ChatRoomService service = new InMemoryChatRoomService(Flux.empty());
+ ChatRoom chatRoom = new ChatRoom(id, name, shard, clock, service, bufferSize);
+ chatRooms.put(id, chatRoom);
+ return Mono.just(chatRoom);
}
-
@Override
public Mono<ChatRoom> getChatRoom(UUID id)
{
- return service
- .getChatRoom(shard, id)
- .switchIfEmpty(Mono.error(() -> new UnknownChatroomException(
- id,
- shard,
- service.getOwnedShards())));
+ return Mono
+ .justOrEmpty(chatRooms.get(id))
+ .switchIfEmpty(Mono.error(() -> new UnknownChatroomException(id)));
}
@Override
public Flux<ChatRoom> getChatRooms()
{
- return service.getChatRooms(shard);
+ return Flux.fromIterable(chatRooms.values());
}
}
import de.juplo.kafka.chat.backend.domain.ChatHome;
import de.juplo.kafka.chat.backend.domain.ChatRoom;
+import de.juplo.kafka.chat.backend.domain.ChatRoomInfo;
import de.juplo.kafka.chat.backend.domain.UnknownChatroomException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
private final ChatRoomChannel chatRoomChannel;
+
+ @Override
+ public Mono<ChatRoomInfo> createChatRoom(UUID id, String name)
+ {
+ log.info("Sending create-command for chat rooom: id={}, name={}");
+ return chatRoomChannel.sendCreateChatRoomRequest(id, name);
+ }
+
@Override
public Mono<ChatRoom> getChatRoom(UUID id)
{
+++ /dev/null
-package de.juplo.kafka.chat.backend.persistence.kafka;
-
-import de.juplo.kafka.chat.backend.domain.ChatRoomFactory;
-import de.juplo.kafka.chat.backend.domain.ChatRoomInfo;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import reactor.core.publisher.Mono;
-
-import java.util.UUID;
-
-
-@RequiredArgsConstructor
-@Slf4j
-public class KafkaChatRoomFactory implements ChatRoomFactory
-{
- private final ChatRoomChannel chatRoomChannel;
-
- @Override
- public Mono<ChatRoomInfo> createChatRoom(UUID id, String name)
- {
- log.info("Sending create-command for chat rooom: id={}, name={}");
- return chatRoomChannel.sendCreateChatRoomRequest(id, name);
- }
-}
chatRoomChannel);
}
- @Bean
- KafkaChatRoomFactory chatRoomFactory(ChatRoomChannel chatRoomChannel)
- {
- return new KafkaChatRoomFactory(chatRoomChannel);
- }
-
@Bean
ChatRoomChannel chatRoomChannel(
ChatBackendProperties properties,
package de.juplo.kafka.chat.backend.persistence;
import de.juplo.kafka.chat.backend.domain.ChatHome;
-import de.juplo.kafka.chat.backend.domain.ChatRoomFactory;
-import de.juplo.kafka.chat.backend.persistence.inmemory.InMemoryChatHomeService;
-import de.juplo.kafka.chat.backend.persistence.inmemory.InMemoryChatRoomFactory;
import de.juplo.kafka.chat.backend.persistence.inmemory.SimpleChatHome;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
{
return new StorageStrategyITConfig()
{
- InMemoryChatHomeService inMemoryChatHomeService = new InMemoryChatHomeService(
- 1,
- new int[] { 0 },
- getStorageStrategy().read());
+ int bufferSize = 8;
- SimpleChatHome simpleChatHome = new SimpleChatHome(inMemoryChatHomeService);
-
- InMemoryChatRoomFactory chatRoomFactory = new InMemoryChatRoomFactory(
- inMemoryChatHomeService,
- chatRoomId -> 0,
+ SimpleChatHome simpleChatHome = new SimpleChatHome(
+ getStorageStrategy().read(),
clock,
- 8);
+ bufferSize);
@Override
public ChatHome getChatHome()
{
return simpleChatHome;
}
-
- @Override
- public ChatRoomFactory getChatRoomFactory()
- {
- return chatRoomFactory;
- }
};
}
}
public abstract class AbstractStorageStrategyIT
{
protected ChatHome chathome;
- protected ChatRoomFactory chatRoomFactory;
protected abstract StorageStrategy getStorageStrategy();
{
StorageStrategyITConfig config = getConfig();
chathome = config.getChatHome();
- chatRoomFactory = config.getChatRoomFactory();
}
protected void stop()
assertThat(chathome.getChatRooms().toStream()).hasSize(0);
UUID chatRoomId = UUID.fromString("5c73531c-6fc4-426c-adcb-afc5c140a0f7");
- ChatRoomInfo info = chatRoomFactory.createChatRoom(chatRoomId, "FOO").block();
+ ChatRoomInfo info = chathome.createChatRoom(chatRoomId, "FOO").block();
log.debug("Created chat-room {}", info);
ChatRoom chatroom = chathome.getChatRoom(chatRoomId).block();
Message m1 = chatroom.addMessage(1l,"peter", "Hallo, ich heiße Peter!").block();
assertThat(chathome.getChatRooms().toStream()).hasSize(0);
UUID chatRoomAId = UUID.fromString("5c73531c-6fc4-426c-adcb-afc5c140a0f7");
- ChatRoomInfo infoA = chatRoomFactory.createChatRoom(chatRoomAId, "FOO").block();
+ ChatRoomInfo infoA = chathome.createChatRoom(chatRoomAId, "FOO").block();
log.debug("Created chat-room {}", infoA);
ChatRoom chatroomA = chathome.getChatRoom(chatRoomAId).block();
Message ma1 = chatroomA.addMessage(1l,"peter", "Hallo, ich heiße Peter!").block();
Message ma4 = chatroomA.addMessage(1l, "klaus", "Ja? Nein? Vielleicht??").block();
UUID chatRoomBId = UUID.fromString("8763dfdc-4dda-4a74-bea4-4b389177abea");
- ChatRoomInfo infoB = chatRoomFactory.createChatRoom(chatRoomBId, "BAR").block();
+ ChatRoomInfo infoB = chathome.createChatRoom(chatRoomBId, "BAR").block();
log.debug("Created chat-room {}", infoB);
ChatRoom chatroomB = chathome.getChatRoom(chatRoomBId).block();
Message mb1 = chatroomB.addMessage(1l,"peter", "Hallo, ich heiße Uwe!").block();
interface StorageStrategyITConfig
{
ChatHome getChatHome();
- ChatRoomFactory getChatRoomFactory();
}
}
{
@Bean
ShardedChatHome chatHome(
- InMemoryChatHomeService chatHomeService)
+ StorageStrategy storageStrategy,
+ Clock clock)
{
SimpleChatHome[] chatHomes = new SimpleChatHome[NUM_SHARDS];
IntStream
.of(ownedShards())
- .forEach(shard -> chatHomes[shard] = new SimpleChatHome(chatHomeService, shard));
+ .forEach(shard -> chatHomes[shard] = new SimpleChatHome(
+ shard,
+ storageStrategy.read(),
+ clock,
+ bufferSize()));
ShardingStrategy strategy = new KafkaLikeShardingStrategy(NUM_SHARDS);
}
@Bean
- InMemoryChatHomeService chatHomeService(
- StorageStrategy storageStrategy)
- {
- return new InMemoryChatHomeService(
- NUM_SHARDS,
- ownedShards(),
- storageStrategy.read());
- }
-
- @Bean
- public FilesStorageStrategy storageStrategy()
+ public FilesStorageStrategy storageStrategy(Clock clock)
{
return new FilesStorageStrategy(
Paths.get("target", "test-classes", "data", "files"),
- Clock.systemDefaultZone(),
- 8,
+ clock,
+ bufferSize(),
new KafkaLikeShardingStrategy(NUM_SHARDS),
messageFlux -> new InMemoryChatRoomService(messageFlux),
new ObjectMapper());
}
+ @Bean
+ Clock clock()
+ {
+ return Clock.systemDefaultZone();
+ }
+
int[] ownedShards()
{
return new int[] { OWNED_SHARD };
}
+
+ int bufferSize()
+ {
+ return 8;
+ }
}
}
static class Configuration
{
@Bean
- SimpleChatHome chatHome(InMemoryChatHomeService chatHomeService)
+ SimpleChatHome chatHome(
+ StorageStrategy storageStrategy,
+ Clock clock)
{
- return new SimpleChatHome(chatHomeService);
+ return new SimpleChatHome(
+ storageStrategy.read(),
+ clock,
+ bufferSize());
}
@Bean
- InMemoryChatHomeService chatHomeService(StorageStrategy storageStrategy)
- {
- return new InMemoryChatHomeService(
- 1,
- new int[] { 0 },
- storageStrategy.read());
- }
-
- @Bean
- public FilesStorageStrategy storageStrategy()
+ public FilesStorageStrategy storageStrategy(Clock clock)
{
return new FilesStorageStrategy(
Paths.get("target", "test-classes", "data", "files"),
- Clock.systemDefaultZone(),
- 8,
+ clock,
+ bufferSize(),
chatRoomId -> 0,
messageFlux -> new InMemoryChatRoomService(messageFlux),
new ObjectMapper());
}
+
+ @Bean
+ Clock clock()
+ {
+ return Clock.systemDefaultZone();
+ }
+
+ int bufferSize()
+ {
+ return 8;
+ }
}
}