/// <summary> /// Indicates if the resultCode and all internal results are successful. /// </summary> /// <param name="resultCode">the command result code</param> /// <param name="results">the individual results</param> /// <returns>true if all is successful</returns> private bool AllResultsOk(Code resultCode, IEnumerable <OpResult> results) { if (resultCode != Code.Ok) { return(false); } foreach (OpResult res in results) { if (res.ErrCode != Code.Ok) { return(false); } if (res.ResultType == OpCode.Multi) { OpResult.RunResult asRunResult = (OpResult.RunResult)res; if (!this.AllResultsOk(asRunResult.ErrCode, asRunResult.Results)) { return(false); } } } return(true); }
/// <summary> /// Moves a command into the inflight tree /// </summary> /// <param name="scheduledCommandPath">the path to move</param> /// <param name="stat">the stat of the node to move</param> /// <returns>the inflight path where the command was moved</returns> private string MoveCommandToInflight(string scheduledCommandPath, IStat stat) { IRingMasterBackendRequest[] setupOps = { new RequestCreate(PathInflightToken, null, new byte[0], new Acl[0], CreateMode.Persistent, null), new RequestMove(scheduledCommandPath, null, stat.Version, PathInflight, null), }; Code resultCode = Code.Unknown; IReadOnlyList <OpResult> results = null; ManualResetEvent e = ManualResetEventPool.InstancePool.GetOne(); try { this.self.Multi( setupOps, (rc, p, c) => { resultCode = (Code)rc; if (resultCode == Code.Ok) { results = p; } e.Set(); }, null, true); } finally { ManualResetEventPool.InstancePool.WaitOneAndReturn(ref e); } if (!this.AllResultsOk(resultCode, results)) { string details = string.Empty; if (results != null) { details = string.Join(",", results.Select(c => c.ErrCode)); } throw new InflightExistException($"Could not move command to inflight: {resultCode}, [{details}]"); } return(this.GetInflightPath(scheduledCommandPath)); }
/// <summary> /// returns the bytes corresponding to the results given /// </summary> /// <param name="resultCode">result code in the response</param> /// <param name="results">Content in the response</param> /// <returns>serialization bytes</returns> public byte[] GetBytes(Code resultCode, IReadOnlyList <OpResult> results) { if (results == null) { throw new ArgumentNullException("results"); } RequestResponse resp = new RequestResponse() { ResultCode = (int)resultCode, Content = results, }; return(this.marshaller.SerializeResponseAsBytes(resp)); }
/// <summary> /// Executes the given command and obtains the result bytes on error. /// </summary> /// <param name="scheduledCommandPath">the path to the command</param> /// <param name="stat">the expected stat of the command node</param> /// <param name="req">the request to execute</param> /// <returns>null on success, or the bytes corresponsing to the execution error</returns> private byte[] ExecuteCommandsAndGetResultsOnError(string scheduledCommandPath, IStat stat, IRingMasterBackendRequest req) { if (req == null) { return(this.GetBytes(Code.Badarguments, null)); } IStat readStat = this.self.Exists(scheduledCommandPath, false, false); if (readStat == null) { throw new InvalidOperationException($"Scheduled command stat of [{scheduledCommandPath}] is null"); } if (readStat.Version != stat.Version || readStat.Cversion != stat.Cversion) { throw new InvalidOperationException( $"Scheduled command stat of [{scheduledCommandPath}] is different from expected command stat; " + $"Expected [{stat.Version} {stat.Cversion}], " + $"Actual [{readStat.Version} {readStat.Cversion}]"); } ManualResetEvent e; Code resultCode = Code.Unknown; IReadOnlyList <OpResult> results = null; // setup the command into the inflight tree string inflightPath = this.MoveCommandToInflight(scheduledCommandPath, stat); // run the command, and atomically remove the inflight marks on success IRingMasterBackendRequest[] ops = { req, new RequestDelete(inflightPath, null, stat.Version, null, DeleteMode.SuccessEvenIfNodeDoesntExist), new RequestDelete(PathInflightToken,null, -1, null, DeleteMode.SuccessEvenIfNodeDoesntExist), }; e = ManualResetEventPool.InstancePool.GetOne(); resultCode = Code.Unknown; results = null; try { this.self.Multi( ops, (rc, p, c) => { resultCode = (Code)rc; if (resultCode == Code.Ok) { results = p; } e.Set(); }, null, true); } finally { ManualResetEventPool.InstancePool.WaitOneAndReturn(ref e); } bool allok = this.AllResultsOk(resultCode, results); if (allok) { // if all went well, we are done since the command was deleted from the inflight tree atomically with the operation. return(null); } // otherwise, we need to undo the setup now this.MoveInflightPathIntoCommandPath(); return(this.GetBytes(resultCode, results)); }
/// <summary> /// moves commands from the inflight path into the command path /// </summary> /// <param name="expectedName">if present, this is the expected name for the single command in the inflight tree. if not present, this method moves all items (not the inflight token) into the commands tree</param> /// <returns>the list of names added to the command tree</returns> private string[] MoveInflightPathIntoCommandPath(string expectedName = null) { string[] children; if (expectedName != null) { children = new string[] { expectedName }; } else { children = this.self.GetChildren(PathInflight, false).ToArray(); } List <string> movedChildren = new List <string>(); if (children.Length == 0) { return(null); } List <IRingMasterBackendRequest> ops = new List <IRingMasterBackendRequest>(); foreach (string child in children) { string childPath = PathInflight + "/" + child; if (childPath == PathInflightToken) { ops.Add(new RequestDelete(childPath, null, -1, null, DeleteMode.SuccessEvenIfNodeDoesntExist)); } else { movedChildren.Add(child); ops.Add(new RequestMove(childPath, null, -1, PathCommands, null)); } } IReadOnlyList <OpResult> results = null; Code resultCode = Code.Unknown; ManualResetEvent e = ManualResetEventPool.InstancePool.GetOne(); try { this.self.Multi( ops.ToArray(), (rc, p, c) => { resultCode = (Code)rc; if (rc == (int)Code.Ok) { results = p; } e.Set(); }, null, true); } finally { ManualResetEventPool.InstancePool.WaitOneAndReturn(ref e); } if (!this.AllResultsOk(resultCode, results)) { string details = string.Empty; if (results != null) { details = string.Join(",", results.Select(c => c.ErrCode)); } throw new InflightExistException($"Could not move command back from inflight: {resultCode}, [{details}]"); } return(movedChildren.ToArray()); }