deepstreamIO / deepstream.io-client-java

The Java/Android Client for deepstream.io
Other
35 stars 37 forks source link

Serious Issues with Login if Deepstream Host is Down #120

Open knocknarea opened 6 years ago

knocknarea commented 6 years ago

https://github.com/deepstreamIO/deepstream.io-client-java/blob/ccadd183a2f989e64fa491ed71ae09f050c09d84/src/main/java/io/deepstream/DeepstreamClient.java#L242

If you attempt to connect to deepstream when the host is down (or not running, for whatever reason)

The DeepStreamClient.login(...) method hangs indefinitely.

There is a countdown latch that needs to be pulled to ZERO before this method can proceed. In the case where you have a connection refused (because the host is unavailable). The code ends up in this deadend and does not call whatever code brings the latch to ZERO.

https://github.com/deepstreamIO/deepstream.io-client-java/blob/ccadd183a2f989e64fa491ed71ae09f050c09d84/src/main/java/io/deepstream/Connection.java#L198

The LoginCallback is never called. It appears that all the methods depend on a response from the server to invoke the callback.

https://github.com/deepstreamIO/deepstream.io-client-java/blob/ccadd183a2f989e64fa491ed71ae09f050c09d84/src/main/java/io/deepstream/DeepstreamClient.java#L317

As an aside the error handlers that you can set into the DeepStreamClient are of little use as the method signature does not contain any reference to the DeepStreamClient that caused the error to happen in the first place.

I don't know if anyone is developing this code anymore but it's a big problem for us. It should be easy to recreate on your side, just try to connect to a host location that is unavailable.

knocknarea commented 6 years ago

Anyone interested in a workaround for this while waiting, I put this together:

public class FastFailDeepstreamClient extends DeepstreamClient implements DeepstreamRuntimeErrorHandler {

    private static final Logger logger = LoggerFactory.getLogger(FastFailDeepstreamClient.class);

    private volatile Thread callingThread;

    /**
     * @param url
     * @throws URISyntaxException
     */
    public FastFailDeepstreamClient(String url) throws URISyntaxException {
        super(url);
        this.setRuntimeErrorHandler(this);
    }

    /**
     * @param url
     * @param options
     * @throws URISyntaxException
     * @throws InvalidDeepstreamConfig
     */
    public FastFailDeepstreamClient(String url, Map<ConfigOptions, Object> options) throws URISyntaxException, InvalidDeepstreamConfig {
        super(url, options);
        this.setRuntimeErrorHandler(this);
    }

    @Override
    public LoginResult login(JsonElement data) {

        //
        // Internal to the login method of DeepstreamClient is a countdown latch that does not get
        // set to ZERO if the deepstream host is unavailable. The login method performs an indefinite
        // await() on this latch leading to a thread hang.
        // We capture the calling thread on the way into the login method. We also have a runtime handler installed
        // if we receive a CONNECTION_ERROR and the calling thread is not null, then we interrupt it so the login method continues
        // to failure.
        //
        this.callingThread = Thread.currentThread();

        LoginResult result = null;

        try {
            result = super.login(data);
            logger.debug("Login to Deepstream was a {}", result != null ? (result.loggedIn() ? "SUCCESS" : "FAILURE") : "FAILURE" );
        }
        finally {
            try {
                Thread.interrupted();
            }
            catch(Exception e) {}
            finally {
                this.callingThread = null;  
            }
        }
        return result;
    }

    /* (non-Javadoc)
     * @see io.deepstream.DeepstreamRuntimeErrorHandler#onException(io.deepstream.Topic, io.deepstream.Event, java.lang.String)
     */
    @Override
    public void onException(Topic topic, Event event, String arg) {
        switch(event) {
        case CONNECTION_ERROR:
            logger.warn("Received Connection Error on Deepstream");
            //
            // This is to get around a bug in DS client 
            // https://github.com/deepstreamIO/deepstream.io-client-java/issues/120
            // 
            if(this.callingThread != null) {
                //
                // Interrupt the code that will lock if there is no connection to deepstream at all (connection refused)
                //
                this.callingThread.interrupt();
            }
            break;
        default:
            break;
        }
    }
}
StephanSchuster commented 6 years ago

Same problem here! Any plans on fixing this (critical) issue?