docs: public void callProcedure(AuthUser user,
                                boolean isAdmin,
                                int timeout,
                                ProcedureCallback cb,
                                String procName,
                                Object[] args)
      {
          // since we know the caller, this is safe
          assert(cb != null);
  
          StoredProcedureInvocation task = new StoredProcedureInvocation();
          task.setProcName(procName);
          task.setParams(args);
          if (timeout != BatchTimeoutOverrideType.NO_TIMEOUT) {
              task.setBatchTimeout(timeout);
          }
  
          InternalAdapterTaskAttributes kattrs = new InternalAdapterTaskAttributes(
                  DEFAULT_INTERNAL_ADAPTER_NAME, isAdmin, connectionId());
  
          assert(m_dispatcher != null);
  
          // JHH: I have no idea why we need to do this, but CL crashes if we don't. Sigh.
          try {
              task = MiscUtils.roundTripForCL(task);
          } catch (Exception e) {
              String msg = String.format("Cannot invoke procedure %s. failed to create task: %s",
                      procName, e.getMessage());
              m_logger.rateLimitedLog(SUPPRESS_INTERVAL, Level.ERROR, null, msg);
              ClientResponseImpl cri = new ClientResponseImpl(ClientResponse.UNEXPECTED_FAILURE, new VoltTable[0], msg);
              try {
                  cb.clientCallback(cri);
              } catch (Exception e1) {
                  throw new IllegalStateException(e1);
              }
          }
  
          createTransaction(kattrs, cb, task, user);
      }