debasishg / scala-redis

A scala library for connecting to a redis server, or a cluster of redis nodes using consistent hashing on the client side.
1.02k stars 219 forks source link

java.lang.Exception: Protocol error: Got ($,[B@1669a570) as initial reply byte during hscan #229

Open Darkhan97 opened 5 years ago

Darkhan97 commented 5 years ago

@debasishg I've got a exception similar to the which is = "java.lang.Exception: Protocol error: Got ($,[B@1669a570) as initial reply byte". I am using Redis server v=4.0.9, clitent "net.debasishg" %% "redisclient" % "3.9". Exception was raised from here !!! val scanTri:Option[(Option[Int], Option[List[Option[String]]])] = try { r.hscan(key, cursor, s"${text}*", 500) } catch { case e:Exception => { println("Cosyak = " + e) None } } How can I deal with this?

Oduig commented 3 years ago

We had this problem and solved it. What appears to be the issue is code like either of the following examples.

val clientOutOfScope = redisPool.withClient { client => 
  client
}
clientOutOfScope.get(...)
redisPool.withClient { client => 
  Future {
    client.get(...)
  }
}

In both cases, the client is used in a situation where it is no longer in the withClient block, either used outside the block or in an asynchronous piece of code. At this point, the withClient block is finished and the client has been returned to the pool. If another thread then tries to use the pool, it may acquire the same client from the pool and the client will be shared across threads. This causes an internal buffer to get mixed up and results in the error above. It can also result in get, hget calls returning the value of a different key than the one requested.

Solution: do not use async code within withClient and never pass the client outside of its body scope.

ShreyasTandale commented 3 years ago

object RedisConnectionUtil extends RedisServiceProvider { lazy val _redisMasterClientPool = new RedisClientPool(redisMasterUrl, 6379) override def getRedisMasterClientPool(): RedisClientPool = _redisMasterClientPool } trait RedisCacheService { def insert(key: CacheKey, values: List[String]): Boolean = { val redisPool = redisConnectionUtil.getRedisMasterClientPool() try { redisPool.withClient(client => { client.auth(redisConnectionUtil.getRedisAuth()) values.foreach(value => { client.lpush(key, value) }) }) logInfo(s"REDIS, push to cache successful for ${key}, values size = ${values.size}") } catch { case e: Exception => { logError(s"Exception while pushing to Redis, key = ${key} message = ${e.getMessage}", e)

  }
}

}

I am using the redis client like this, still getting above error. Can you help me find out what is the issue?

Oduig commented 3 years ago

I don't see any problem with that code... Perhaps consider writing a minimal example with a test, and posting it to GitHub so it's reproducible.

debasishg commented 3 years ago

Yes, a minimal reproducible test case will help debug the problem.

debasishg commented 3 years ago

@Oduig Indeed .. a client escaping the scope of withClient is a problem.

noahlz commented 2 years ago

I just bumped into this issue because I was sending a list of items to redis in batches with list.grouped(500)... but forgot that grouped returns an Iterator that is "lazy" and doesn't comprehend the list until you call .toList. I was doing that outside the withClient block and therefore this error occurred. Very subtle!

choijiho0021 commented 1 year ago

@Oduig Thank you. it working,

// "Sometimes" Protocol error found code
  private val redisClient = pool.withClient { client => client }

  def jwtBlackList(key: Long, authObject: String) = {
    redisClient.rpush(key, authObject)
    redisClient.expire(key, 240)
  }

use only withClient "inside".

// Code changed after seeing your GitHub answer
  def jwtBlackList(key: Long, authObject: String) = {
    pool.withClient { redisClient =>
      redisClient.rpush(key, authObject)
      redisClient.expire(key, 240)
    }
  }

Now my code doesn't cause protocol errors.