code: @Override
    public void end(Xid xid, int flags) throws XAException {
      if (logger.logDebug()) {
        debug("ending transaction xid = " + xid);
      }
  
      // Check preconditions
  
      if (flags != XAResource.TMSUSPEND && flags != XAResource.TMFAIL
          && flags != XAResource.TMSUCCESS) {
        throw new CloudSpannerXAException(CloudSpannerXAException.INVALID_FLAGS,
            Code.INVALID_ARGUMENT, XAException.XAER_INVAL);
      }
  
      if (xid == null) {
        throw new CloudSpannerXAException(CloudSpannerXAException.XID_NOT_NULL, Code.INVALID_ARGUMENT,
            XAException.XAER_INVAL);
      }
  
      if (state != STATE_ACTIVE || !currentXid.equals(xid)) {
        throw new CloudSpannerXAException(CloudSpannerXAException.END_WITHOUT_START,
            Code.FAILED_PRECONDITION, XAException.XAER_PROTO);
      }
  
      // Check implementation deficiency preconditions
      if (flags == XAResource.TMSUSPEND) {
        throw new CloudSpannerXAException(CloudSpannerXAException.SUSPEND_NOT_IMPLEMENTED,
            Code.UNIMPLEMENTED, XAException.XAER_RMERR);
      }
  
      // We ignore TMFAIL. It's just a hint to the RM. We could roll back
      // immediately
      // if TMFAIL was given.
  
      // All clear. We don't have any real work to do.
      state = STATE_ENDED;
    }
docs: Preconditions: 1. Flags is one of TMSUCCESS, TMFAIL, TMSUSPEND 2. xid != null 3. Connection is
  associated with transaction xid
  
  Implementation deficiency preconditions: 1. Flags is not TMSUSPEND
  
  Postconditions: 1. connection is disassociated from the transaction.
  
  @see XAResource#end(Xid, int)