nuecho / rivr

Rivr is a lightweight open-source dialogue engine enabling Java developers to easily create enterprise-grade VoiceXML applications.
http://rivr.nuecho.com
Other
61 stars 23 forks source link

Support session replication for HA deployments #8

Closed maximdim closed 5 years ago

maximdim commented 5 years ago

I'm looking at the way to deploy code that uses rivr library in the multi-node environment for high availability purposes - the requirement is for a conversation session to survive a node crash/restart.

From what I can see DialogueServlet has support for Servlet's HttpSession and would save a Session object in HttpSession, which could be made persistent or stored out-of process (via spring-session library, for example). In my tests, when configured properly HttpSession would survive node restart, but if I read code correctly SessionContainer never reads from HttpSession - only from local sessions map (mSessions) so even so HttpSession is present after restart conversation cannot continue because of SessionNotFoundException.

I just want to confirm that what I need is indeed not supported by the current implementation? Thanks.

gawi commented 5 years ago

You are correct. In fact the HttpSession is not really used as a mean to store the dialog state. Instead, it is used to trigger the JavaEE session tracking mechanisms (URL rewritting/JSESSIONID cookie).

The state of the dialog is maintained in the Java call stack. Is it not possible to externalize it. It's a constraint under which you must live when using Rivr.

You can do some partial recovery of the call through the VoiceXmlDialogueContext.setFatalErrorFormFactory() method. If you create a fatal error form containing a <goto> to your dialog URI (assuming this URI is handled by a load blanacer), you could restart your dialog on another node.

Here's a sample FatalErrorFormFactory:

package com.nuecho.rivr.demo.dialogue;

import static com.nuecho.rivr.core.util.DomUtils.*;
import static com.nuecho.rivr.voicexml.rendering.voicexml.VoiceXmlDomUtil.*;

import org.w3c.dom.*;

import com.nuecho.rivr.voicexml.dialogue.*;
import com.nuecho.rivr.voicexml.rendering.voicexml.errorhandling.*;
import com.nuecho.rivr.voicexml.turn.*;

public class RecoveryFatalErrorFormFactory implements FatalErrorFormFactory {

  private final String mServletPath;
  private final String mContextPath;

  public RecoveryFatalErrorFormFactory(VoiceXmlDialogueContext context) {
    mServletPath = context.getServletPath();
    mContextPath = context.getContextPath();
  }

  @Override
  public void addFatalErrorForm(Element form, VoiceXmlDocumentTurn turn) {
    Element blockElement = appendNewElement(form, BLOCK_ELEMENT);
    Element gotoElement = appendNewElement(blockElement, GOTO_ELEMENT);

    String initialServletUri = mContextPath + mServletPath;
    gotoElement.setAttribute(NEXT_ATTRIBUTE, initialServletUri + "?recovery=true");
  }
}

In fact, you should also ensure that an error occurring during error handling will not loop indefinitely. So you should probably do something like:

  public void addFatalErrorForm(Element form, VoiceXmlDocumentTurn turn) {
    Element catchElement = appendNewElement(form, CATCH_ELEMENT);
    catchElement.setAttribute(EVENT_ATTRIBUTE, ERROR_EVENT_NAME);
    Element promptElement = appendNewElement(catchElement, "prompt");
    appendNewText(promptElement, "A fatal error occured.  Please try to call back later.");
    appendNewElement(catchElement, EXIT_ELEMENT);

    Element blockElement = appendNewElement(form, BLOCK_ELEMENT);
    Element gotoElement = appendNewElement(blockElement, GOTO_ELEMENT);

    String initialServletUri = mContextPath + mServletPath;
    gotoElement.setAttribute(NEXT_ATTRIBUTE, initialServletUri + "?recovery=true");
  }

Of course, from there, you can go further and pass some elements of the dialog state in the URI and try to resume the dialog with the provided information. If you have a simple callflow, it might be a viable option. It might not provide a totally transparent failure recovery but it's better than hanging up.

maximdim commented 5 years ago

Thank you very much for the detailed response. I appreciate your help.