New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Error : java.lang.Long cannot be cast to [B" when trying evalsha command with Pipeline #916

Closed
mayankdang opened this Issue Mar 9, 2015 · 18 comments

Comments

Projects
None yet
9 participants
@mayankdang
Contributor

mayankdang commented Mar 9, 2015

I know there are several "similar" issues, but none of the errors reported earlier (#663, for example) is the same as the one I am getting.
Versions: Jedis 2.6.2
Maven dependency :

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.6.2</version>
</dependency> 

Redis Versions: Tested with both current unstable branch (3.1.999) and official release 2.8.13.

Code :

public List<Object> wowDude(int db, int choice) throws Exception {
        Jedis jedis = null;
        jedis = getJedis(db, choice, jedis);
        Pipeline pipeline = jedis.pipelined();
        pipeline.del("ping");
        pipeline.evalsha("4685a09f15d2b2532b034d340b80004349e4462b", 1, "lol", "omg245324", "___");
        pipeline.del("omg2");
        List<Object> list = pipeline.syncAndReturnAll();
        returnJedis(choice, jedis);
        return list;
}

where getJedis and returnJedis are simple methods that call getResourse() and returnResource() from / to the JedisPool, a static variable.

The lua script code is

-- first ensure number of keys to be 1 and number of arguments to be 2
-- first key - hash's name
-- first argument - key value
-- second argument - value to append
if ((table.getn(ARGV) == 2) and (table.getn(KEYS) == 1)) then
    if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
        local val = redis.call("HGET", KEYS[1], ARGV[1])
        return redis.call("HSET", KEYS[1], ARGV[1], val..","..ARGV[2])
    else
        return redis.call("HSET", KEYS[1], ARGV[1], ARGV[2])
    end
end

and the sha1being passed through jedis is the same as the script load redis command and the sha1sum linux command of the above lua script.

The stack trace is:

ERROR com.testSuite.servlets.TestServlet3 [] -java.lang.Long cannot be cast to [B
" java.lang.ClassCastException: java.lang.Long cannot be cast to [B
    at redis.clients.jedis.BuilderFactory$5.build(BuilderFactory.java:56) ~[jedis-2.6.2.jar:?]
    at redis.clients.jedis.BuilderFactory$5.build(BuilderFactory.java:54) ~[jedis-2.6.2.jar:?]
    at redis.clients.jedis.Response.build(Response.java:50) ~[jedis-2.6.2.jar:?]
    at redis.clients.jedis.Response.get(Response.java:35) ~[jedis-2.6.2.jar:?]
    at redis.clients.jedis.Pipeline.syncAndReturnAll(Pipeline.java:105) ~[jedis-2.6.2.jar:?]
    at com.testSuite.utils.JedisClient.wowDude(JedisClient.java:317) ~[JedisClient.class:?]
    at com.testSuite.servlets.TestServlet3.doGet(TestServlet3.java:51) [TestServlet3.class:?]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) [servlet-api.jar:?]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728) [servlet-api.jar:?]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) [catalina.jar:7.0.47]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) [catalina.jar:7.0.47]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) [tomcat7-websocket.jar:7.0.47]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) [catalina.jar:7.0.47]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) [catalina.jar:7.0.47]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) [catalina.jar:7.0.47]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) [catalina.jar:7.0.47]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [catalina.jar:7.0.47]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) [catalina.jar:7.0.47]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) [catalina.jar:7.0.47]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) [catalina.jar:7.0.47]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) [catalina.jar:7.0.47]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041) [tomcat-coyote.jar:7.0.47]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603) [tomcat-coyote.jar:7.0.47]
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) [tomcat-coyote.jar:7.0.47]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [?:1.7.0_25]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [?:1.7.0_25]
    at java.lang.Thread.run(Thread.java:724) [?:1.7.0_25]

PS. The call succeeds (the value gets appended to the value of the key in the hash specified, but I get the error, while the list is being returned to me by syncAndReturnAll() function.
PPS. The error is not received if I call sync() instead of syncAndReturnAll() of Pipeline.java.

Also,
The error doesn't come if the lua script is modified to:

if ((table.getn(ARGV) == 2) and (table.getn(KEYS) == 1)) then
    if redis.call("HEXISTS", KEYS[1], ARGV[1]) == 1 then
        local val = redis.call("HGET", KEYS[1], ARGV[1])
        redis.call("HSET", KEYS[1], ARGV[1], val..","..ARGV[2])
    else
        redis.call("HSET", KEYS[1], ARGV[1], ARGV[2])
    end
end
return "str"  -- any constant string
@HeartSaVioR

This comment has been minimized.

Collaborator

HeartSaVioR commented Mar 9, 2015

It's known bug and already fixed by #607.
But it requires change of method signature, so with semver we're planning to apply fix to 3.0.0.

@xetorthio @marcosnils @nykolaslima
Maybe we should release 3.0.0 earlier cause it fixes some method signature issues which shows strange behavior.

@HeartSaVioR

This comment has been minimized.

Collaborator

HeartSaVioR commented Mar 9, 2015

@mayankdang
Currently (and maybe 2.x) eval* in Pipeline only work with string result, so you're encouraged to use eval without Pipeline.
Sorry for inconvenience. ;(

@nykolaslima

This comment has been minimized.

Contributor

nykolaslima commented Mar 9, 2015

huum...I'm not sure about it. Because if we release the 3.0.0, after it we will change the entire structure and will need to name it 4.0.

Since it's a bug fix, shouldn't be better to apply it to 2.7 even knowing it will change the method signature?
I believe it's better than let a known bug opened.

@HeartSaVioR

This comment has been minimized.

Collaborator

HeartSaVioR commented Mar 10, 2015

@nykolaslima
We will try our best to apply any breaking changes before releasing 3.0.0. So don't worry about 4.0 yet.

Btw, keeping compatibility between minor releases is very important.
I don't like such libraries which often break compatibility so I need to rewrite code when I update the library just for bugfix.

But it's important to let users know we're not recommended to use these methods now.
So how about this approach?
We can deprecate methods at 2.x with enough description, and restore methods at 3.0.0.

@xetorthio @marcosnils

@nykolaslima

This comment has been minimized.

Contributor

nykolaslima commented Mar 10, 2015

But will we release 3.0.0 before other changes?

@HeartSaVioR

This comment has been minimized.

Collaborator

HeartSaVioR commented Mar 10, 2015

@nykolaslima
We're already having issues and PRs with 3.0.0 milestone.
We will check milestone again when we feel it's time to release.

@marcosnils marcosnils added this to the 3.0.0 milestone May 26, 2015

@sradeep-d

This comment has been minimized.

sradeep-d commented Jan 3, 2017

Hi @HeartSaVioR ,
I am presently using version 2.9.0 with the issue "java.lang.Long cannot be cast to [B".
Would like to the plan of releasing the fix.

@inponomarev

This comment has been minimized.

inponomarev commented Jan 8, 2017

Hello, all! I am using 2.8.1 and recently started to get ClassCastExceptions both ways:

java.lang.ClassCastException: [B cannot be cast to java.lang.Long
at redis.clients.jedis.Connection.getIntegerReply(Connection.java:222)
at redis.clients.jedis.Jedis.ttl(Jedis.java:308)

java.lang.ClassCastException: java.lang.Long cannot be cast to [B
at redis.clients.jedis.Connection.getBinaryBulkReply(Connection.java:216)
at redis.clients.jedis.Connection.getBulkReply(Connection.java:205)
at redis.clients.jedis.Jedis.hget(Jedis.java:622)

Everything worked fine until recent. Could you suggest what's happening?

@marcosnils

This comment has been minimized.

Collaborator

marcosnils commented Jan 8, 2017

@inponomarev this has been discussed extensively in the mailing lists and GH issues. Please refer to those sources to better diagnose your problem and find possible fixes

@inponomarev

This comment has been minimized.

inponomarev commented Jan 21, 2017

@marcosnils, thanks for your answer, I ran through all the web pages with 'cannot be cast to [B' phrase. I updated to 2.9.0. I use JedisPool and .close() in 'finally' block to return connections. I use setTestOnBorrow(true) in JedisPool configuration. Still the problem occurs from time to time.

I do admit it might be my fault, but the problem is that 'something cannot be cast to [B' message is not informative and it does not help to diagnose the issue. And as far as we can see from Google search, the same message puzzles many people. Maybe something could be done with this? Can it be more informative?

@ogasawaraShinnosuke

This comment has been minimized.

ogasawaraShinnosuke commented Jul 3, 2017

pom.xml

<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>

The data I want is below. Also, the LIST type is actually included in Redis.

// I want type
List<String>

// actually data
example.com:6379> lrange key 0 10
1) "1"
2) "2"
3) "3"
4) "4"

When extracting from the LIST type, it is trying to convert it to the LONG side why.

Jedis jedis = // get Jedis instance
String cacheKey = "key";
Long len = jedis.llen(cacheKey);
if (len != null && len >= 1) List<String> list = jedis.lrange(cacheKey, 0, len);
due to exception [java.lang.Long cannot be cast to java.util.List]
java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List
	at redis.clients.jedis.Connection.getBinaryMultiBulkReply(Connection.java:276) ~[jedis-2.9.0.jar:?]
	at redis.clients.jedis.Connection.getMultiBulkReply(Connection.java:269) ~[jedis-2.9.0.jar:?]
	at redis.clients.jedis.Jedis.lrange(Jedis.java:935) ~[jedis-2.9.0.jar:?]

I do not know if it relates to this issue, if you know who caused the problem, would you please teach me?

@ogasawaraShinnosuke

This comment has been minimized.

ogasawaraShinnosuke commented Aug 18, 2017

Does anyone know about the above matter?

@inponomarev

This comment has been minimized.

inponomarev commented Aug 19, 2017

I had to investigate the issue myself debugging through Jedis code.

This issue occurs when Jedis receives an unexpected answer from Redis server, e. g.: Jedis tries to GET a value and expects binary answer, but instead it receives an integer from TTL call made by someone else.

The most probable cause is that one Jedis object is shared between multiple threads. Check your code thoroughly for this. But from time to time these problems occur with no visible cause. I use

poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnCreate(true);

This forces Jedis to call PING every time the connection is requested from pool and in case this exception occurs, just reconnect. This seems to me like suppressing the real problem. There is a problem with Jedis. At least something can be done to make 'Foo cannot be cast to Bar' exception more informative.

@ogasawaraShinnosuke

This comment has been minimized.

ogasawaraShinnosuke commented Aug 19, 2017

I saw https://github.com/xetorthio/jedis/wiki/Getting-started#using-jedis-in-a-multithreaded-environment and wrote below code.

try (Jedis jedis = pool.getResource()) {
// jedis actions
}
@yhari

This comment has been minimized.

yhari commented Dec 18, 2017

Facing same issue as @inponomarev. Version 2.9.0. Happens with certain level of concurrency. It happens very intermittently and I couldn't replicate

@sazzad16

This comment has been minimized.

Collaborator

sazzad16 commented Jan 21, 2018

First of all, I have no idea about lua. Please do correct me if I'm wrong.

@mayankdang From your code:

if condition_1 then
    if condition_2 then
        return
    else
        return
    end
end --> no else, no return

it seems like it's possible that the script would return nothing. So Response object of evalsha is trying to consume something which is actually the return of next command, del (which is Long). In that case, your exception is very much possible at

return data == null ? null : SafeEncoder.encode((byte[]) data);

@HeartSaVioR @nykolaslima @marcosnils WDYT?

@marcosnils

This comment has been minimized.

Collaborator

marcosnils commented Jan 21, 2018

@sazzad16 according to the example in the first post, the lua script always returns a value. @HeartSaVioR said that this was fixed in the master branch here #607. I'll close this as the issue is already fixed.

@sazzad16

This comment has been minimized.

Collaborator

sazzad16 commented Jan 21, 2018

@marcosnils I was also considering that

The error doesn't come if the lua script is modified to (return any constant string)

However, it's closed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment