본문 바로가기

개발중/Spring Boot & Redis

[Spring - Redis] RedisReadOnlyException: READONLY You can't write against a read only replica. Master kill 시 에러 ( master / slave 구조임 )

728x90
반응형


에러 내용

 

## master 죽였을 때
Caused by: io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica.
 at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:144) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.lettuce.core.internal.ExceptionFactory.createExecutionException(ExceptionFactory.java:116) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.lettuce.core.protocol.AsyncCommand.completeResult(AsyncCommand.java:120) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.lettuce.core.protocol.AsyncCommand.complete(AsyncCommand.java:111) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.lettuce.core.protocol.CommandHandler.complete(CommandHandler.java:747) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.lettuce.core.protocol.CommandHandler.decode(CommandHandler.java:682) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.lettuce.core.protocol.CommandHandler.channelRead(CommandHandler.java:599) ~[lettuce-core-6.1.10.RELEASE.jar!/:6.1.10.RELEASE]
 at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.94.Final.jar!/:4.1.94.Final]
 at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.94.Final.jar!/:4.1.94.Final]
 ... 1 common frames omitted.

 

아래와 같이 master 1 개와  slave 1 개로 구성이 되어있다고 가정합니다.

 

master 가 node 가 죽었든 pod 가 죽었든 죽어버렸습니다. ( 이때 에러 발생 )

 

slave 중 1 개가 master 로 승격이 됩니다.

slave 는 하나가 되었기 때문에 slave 하나가 재생성 됩니다.

 

spring 에 redis 설정은 아래와 같이 되어있습니다.

 

@Configuration
public class WriteToMasterReadFromReplicaConfiguration {

    final String hostName = "redis";
    final String password = "1234!@#";
    final int port = 6379;


    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {

        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                .readFrom(ReadFrom.REPLICA_PREFERRED)
                .build();

        RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration(hostName, port);
        serverConfig.setPassword(RedisPassword.of(password));

        return new LettuceConnectionFactory(serverConfig, clientConfig);
    }
}

 

readFrom 이라는 속성에 대해 알아보았습니다.

 

ReadFrom.REPLICA

  • 오직 레플리카 노드에서만 읽기를 수행합니다.

 

ReadFrom.REPLICA_PREFERRED

  • 가능하면 Slave (Replica) 노드에서 읽기를 수행합니다.
  • Slave 노드가 사용 불가능한 경우에만 Master 노드에서 읽기를 수행합니다.
  • 이 옵션을 사용하면 읽기 부하를 Master 노드에서 분산시킬 수 있지만, Master가 다운되고 새로운 Master가 선출되는 시간 동안 문제가 발생할 수 있습니다.

 

ReadFrom.MASTER

  • 오직 마스터 노드에서만 읽기를 수행합니다.

 

ReadFrom.MASTER_PREFERRED

  • 가능하면 Master 노드에서 읽기를 수행합니다.
  • Master 노드가 사용 불가능한 경우에만 Slave (Replica) 노드에서 읽기를 수행합니다.
  • 이 설정은 Master의 데이터가 가장 최신이며 일관성이 있기 때문에 읽기 작업의 정확성을 높이는 데 도움이 됩니다. 그러나 이렇게 하면 모든 읽기 요청이 Master로 전달되므로 Master 노드에 부하가 집중될 수 있습니다.


두 옵션은 둘 다 읽기 작업의 탄력성을 높이려는 목적으로 사용되지만, 어느 노드에 부하를 더 주고 싶은지에 따라 선택할 수 있습니다.

REPLICA_PREFERRED는 읽기 부하를 Master에서 분산시키려 할 때 유용하며, MASTER_PREFERRED는 데이터의 일관성과 정확성을 높이려 할 때 유용합니다.

 

근데 제 상황에서는 MASTER 에게 부하를 주는 부담을 주더라도 ReadFrom.MASTER_PREFERRED 옵션을 선택해야 할 것 같습니다.

 

쓰기에 대한 옵션을 제가 지정하고 싶다는 생각을 했습니다. ( MASTER 에게만 쓰라 그러면 READ_ONLY 에러는 발생하지 않을테니 ;; ) 왜 slave 에게 쓰라는 이벤트를 해서 에러를 내는지 이해가 가지는 않습니다만 ..

 


 

sentinel 을 사용하는 경우에는 spring boot 설정에서 아래와 같은 설정이 필요하다고 합니다.

 

@Bean
public RedisConnectionFactory redisConnectionFactory() {
    RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
            .master("mymaster")
            .sentinel("redis-node-2.redis-headless.lucy3.svc.cluster.local", 26379)
            .sentinel("redis-node-1.redis-headless.lucy3.svc.cluster.local", 26379);
 
    sentinelConfig.setPassword(RedisPassword.of(password));

    // 마스터에서 우선적으로 읽지만, 마스터가 응답하지 않을 경우 슬레이브에서 읽음
    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
            .readFrom(ReadFrom.MASTER_PREFERRED)
            .build();

    return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}

 

 

 

고성능을 위한 Redis (High Availability, HA)와 Kubernetes 구축 마스터 가이드 (Spring Session storage 설정까지)

 

고성능을 위한 Redis (High Availability, HA)와 Kubernetes 구축 마스터 가이드 (Spring Session storage 설정까지)

목표 1. Redis k8s 구성 이해 Redis Sentinel 고가용성(High Availability, HA) 클러스터 구성 방식으로 설계하였습니다. Master Redis Server 실제 데이터를 저장하고 처리하는 주 서버입니다. Slave Redis Server Master

soobindeveloper8.tistory.com

 

728x90
반응형