/// <summary> /// Initializes a new instance of the <see cref="SecondaryPreprocessor"/> class. /// </summary> /// <param name="backend">Backend core</param> /// <param name="runOnTxIdMultiplesOf">The action will be run whenever txId % runOnTxIdMultiplesOf == 0.</param> /// <param name="runOnTxIdMultiples">The action to run whenever txId % runOnTxIdMultiplesOf == 0.</param> public SecondaryPreprocessor(RingMasterBackendCore backend, int runOnTxIdMultiplesOf, RunOnTxIdMultiplesFunction runOnTxIdMultiples) { if (backend == null) { throw new ArgumentNullException(nameof(backend)); } this.backend = backend; if (runOnTxIdMultiplesOf < 0) { throw new ArgumentException("runOnTxIdMultiplesOf cannot be < 0"); } this.runOnTxIdMultiplesOf = runOnTxIdMultiplesOf; this.lastTxRun = 0; this.runOnTxIdMultiples = runOnTxIdMultiples; this.thisCommandPathAllReplicas = this.backend.ReplicaCommandPathPrefix + "/$$<all>"; this.thisCommandPathAllSecondaries = this.backend.ReplicaCommandPathPrefix + "/$$<sec>"; this.thisCommandPathOneSecondary = this.backend.ReplicaCommandPathPrefix + "/$$<onesec>"; this.thisCommandPathPrimary = this.backend.ReplicaCommandPathPrefix + "/$$<prim>"; Trace.TraceInformation("this.runOnTxIdMultiples={0}", this.runOnTxIdMultiplesOf); }
/// <summary> /// Initializes a new instance of the <see cref="RingMasterRequestExecutor"/> class. /// </summary> /// <param name="backend">RingMaster backend</param> /// <param name="configuration">Configuration settings</param> /// <param name="instrumentation">Instrumentation consumer</param> /// <param name="cancellationToken">Token to be observed for cancellation signal</param> public RingMasterRequestExecutor(RingMasterBackendCore backend, Configuration configuration, IInstrumentation instrumentation, CancellationToken cancellationToken) { this.backend = backend ?? throw new ArgumentNullException(nameof(backend)); this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); this.instrumentation = instrumentation; this.parentCancellationToken = cancellationToken; }
/// <summary> /// Initializes a new instance of the <see cref="LoopbackRingMaster"/> class. /// </summary> /// <param name="backend">Backend core</param> /// <param name="readOnlyInterfaceRequiresLocks">Whether readonly interface requires locks</param> /// <param name="allowWrites">Whether write operation is allowed</param> public LoopbackRingMaster(RingMasterBackendCore backend, bool readOnlyInterfaceRequiresLocks, bool allowWrites) : base("loopback:0", 0, null) { this.backend = backend ?? throw new ArgumentNullException("backend"); this.session = backend.GetLoopbackSession(string.Empty, false, allowWrites, readOnlyInterfaceRequiresLocks); this.executionQueue = new ExecutionQueue(this.maxThreads); this.session.ROInterfaceRequiresLocks = readOnlyInterfaceRequiresLocks; }
/// <inheritdoc /> public override void Close() { if (this.executionQueue == null) { return; } this.executionQueue.Drain(ExecutionQueue.DrainMode.DisallowAllFurtherEnqueues); this.session.Close(); this.backend = null; }
/// <summary> /// Initializes a new instance of the <see cref="PseudoNodes"/> class. /// </summary> /// <param name="backend">the backend to use</param> /// <param name="getSettingFunction">Function to get settings</param> public PseudoNodes(RingMasterBackendCore backend, Func <string, string> getSettingFunction) { if (getSettingFunction == null) { getSettingFunction = (s) => null; } this.getSetting = getSettingFunction; this.backend = backend; this.serviceHealingMgr = new ServiceHealingManager(this); }
/// <summary> /// Gets a client to the primary. /// </summary> /// <param name="backend">The backend.</param> /// <param name="port">The port.</param> /// <param name="ssl">The SSL.</param> /// <returns>the new client to the primary</returns> internal static RingMaster GetClientToPrimary(RingMasterBackendCore backend, int port, SslWrapping ssl) { if (backend == null) { throw new ArgumentNullException("backend"); } // addr will contain the list of peers, or '127.0.0.1' if none is there string addr = string.Empty; ClusterMember[] cm = backend.Factory.GetAgreedMembers(); if (cm != null) { foreach (ClusterMember m in cm) { if (addr == string.Empty) { addr = string.Format("{0}:{1}", m.Address, port); } else { addr = string.Format("{0};{1}:{2}", addr, m.Address, port); } } } else { // nothing on cm, just us. addr = "127.0.0.1:" + port; } Trace.TraceInformation("SetupPseudoNodes {0} ", addr); RingMaster rm = new RingMaster(addr, 1000, null); rm.AddAuthInfo(AuthSchemes.Digest, "root"); rm.SetSsl(ssl); return(rm); }
/// <summary> /// Initializes a new instance of the <see cref="RMCommands"/> class. /// </summary> /// <param name="be">Backend core</param> public RMCommands(RingMasterBackendCore be) { this.backend = be; }
/// <summary> /// runs the command encoded in the given path /// </summary> /// <param name="path">the command encoded in the path</param> /// <param name="data">optionally, the data associated with the request</param> /// <param name="session">Client session</param> /// <param name="lockList">the lock list of the invocation</param> /// <param name="requestedCommand">Requested command on return</param> /// <param name="content">More info on the command on return</param> /// <returns>code for possible error</returns> public Code RunCommandPath(string path, byte[] data, ClientSession session, ILockListTransaction lockList, out string requestedCommand, out object content) { if (path == null) { throw new ArgumentNullException(nameof(path)); } Code result = Code.Apierror; requestedCommand = "unknown"; content = "unknown command"; if (!this.IsCommand(path)) { return(result); } string[] pieces = path.Split('/'); if (pieces.Length >= 2) { requestedCommand = pieces[1].ToLower(); if (!this.IsCommandAllowed(session, requestedCommand)) { content = "command not allowed now"; return(Code.Apierror); } RingMasterEventSource.Log.RunCommand(requestedCommand); this.backend.auditConsumer?.OnRunCommand(requestedCommand); switch (requestedCommand) { case "getsetting": { content = RingMasterBackendCore.GetSetting(pieces[2]); result = Code.Ok; break; } case "gc": { GC.Collect(); content = "GC scheduled"; result = Code.Ok; break; } case "takecheckpoint": { bool ok = this.backend.Factory.TakeCheckpoint(); content = string.Format("Checkpoint {0}scheduled", ok ? string.Empty : "not "); result = Code.Ok; break; } case "buildtreefile": { string commandResult; string[] args = Encoding.UTF8.GetString(data).Split('|'); try { int ver = int.Parse(args[2]); RequestResponse resp = this.backend.BuildTreeFile(nodepath: args[0], filename: args[1], version: ver); commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed"; result = (Code)resp.ResultCode; } catch (Exception e) { commandResult = "failed:" + e.Message; result = Code.Systemerror; } content = string.Format("buildtreefile: nodepath={0} filename={1} result={2}", args[0], args[1], commandResult); break; } case "mount": { string commandResult; string[] args = Encoding.UTF8.GetString(data).Split('|'); try { RequestResponse resp = this.backend.Mount(nodepath: args[0], filename: args[1], allowremount: false); commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed"; result = (Code)resp.ResultCode; } catch (Exception e) { commandResult = "failed:" + e.Message; result = Code.Unknown; } content = string.Format("mount: nodepath={0} filename={1} result={2}", args[0], args[1], commandResult); break; } case "remountroot": { string commandResult; string filepath = Encoding.UTF8.GetString(data); try { RequestResponse resp = this.backend.Mount(nodepath: "/", filename: filepath, allowremount: true, mountRoot: true); commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed"; result = (Code)resp.ResultCode; } catch (Exception e) { commandResult = "failed:" + e.Message; result = Code.Unknown; } content = string.Format("mountroot: nodepath=/ filename={0} result={1}", filepath, commandResult); break; } case "unmount": { string commandResult; string[] args = Encoding.UTF8.GetString(data).Split('|'); try { RequestResponse resp = this.backend.Unmount(args[0]); commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed"; result = (Code)resp.ResultCode; } catch (Exception e) { commandResult = "failed:" + e.Message; result = Code.Unknown; } content = string.Format("mount: nodepath={0} result={1}", args[0], commandResult); break; } case "remount": { string commandResult; string[] args = Encoding.UTF8.GetString(data).Split('|'); try { RequestResponse resp = this.backend.Mount(nodepath: args[0], filename: args[1], allowremount: true); commandResult = resp.ResultCode == (int)Code.Ok ? "success" : "failed"; result = (Code)resp.ResultCode; } catch (Exception e) { commandResult = "failed:" + e.Message; result = Code.Unknown; } content = string.Format("remount: nodepath={0} result={1}", args[0], commandResult); break; } case "measure": { StringBuilder sb = new StringBuilder(); Dictionary <string, object> results; try { this.backend.CanMeasure = true; ((IUnsafeTreeAccess)this.backend).LockRootNoSync(); results = this.backend.MeasureFullTree(PlatformHelper.ProcessorCount, maxTimeForMeasurementInMillis); } finally { this.backend.CanMeasure = false; ((IUnsafeTreeAccess)this.backend).ReleaseRoot(); } if (results != null) { foreach (KeyValuePair <string, object> line in results) { string text; string child_i = line.Key; Exception e = line.Value as Exception; if (e != null) { text = string.Format("Metrics for {0}: {1}", child_i, e); } else { ulong metrics = (ulong)line.Value; text = string.Format("Metrics for {0} is {1:X}", child_i, metrics); } sb.AppendLine(text); } } content = sb.ToString(); result = Code.Ok; break; } case "getlocaltime": { content = string.Format("local time: " + DateTime.UtcNow.ToString("o")); result = Code.Ok; break; } case "getcommands": { content = this.GetCommands(); result = Code.Ok; break; } case "trace": { string message = "<null>"; if (data != null) { message = Encoding.UTF8.GetString(data); } content = "traced: " + message; result = Code.Ok; Trace.TraceInformation(message); break; } case "failoverinto": { string replica = "<any>"; if (data != null) { replica = Encoding.UTF8.GetString(data); } content = "Failing over into: " + replica + " in 10 seconds"; this.backend.FailoverInto(replica, 10000); result = Code.Ok; break; } case "downloadurlintolocation": { string arguments = null; if (data != null) { arguments = Encoding.UTF8.GetString(data); } // we run this one command asynchronously with any potential replication bool hasStarted = this.backend.DownloadUrlIntoLocation(arguments); if (hasStarted) { content = string.Format("DownloadUrlIntoLocation started arguments={0}", arguments); result = Code.Ok; } else { content = string.Format("DownloadUrlIntoLocation functionality is not implemented"); result = Code.Unimplemented; } break; } case "poisonpill": { string spec = null; if (data != null) { spec = Encoding.UTF8.GetString(data); } RequestResponse res = this.GeneratePoisonPill(spec, session, lockList); if (res != null) { content = string.Format("poison pill executed. Result = {0}", res.Content); result = (Code)res.ResultCode; } else { content = "Poison pills not allowed from config"; result = Code.Authfailed; } break; } case "importcheckpoint": { string location = null; if (data != null) { location = Encoding.UTF8.GetString(data); } RingMasterThreadPool.Instance.QueueUserWorkItem(_ => { Thread.Sleep(5000); bool ok = this.backend.ImportCheckpoint(location); Trace.TraceWarning("Import checkpoint {0}: {1}", location, ok ? "Succeeded" : "Failed"); if (ok) { Trace.TraceWarning("This instance will die now"); Environment.Exit(1); } }); content = "Importing checkpoint " + location + "file and restarting"; result = Code.Ok; break; } case "getstats": { lock (this.SyncObject) { content = string.Format("PersistentData.TotalNodes={0} PersistentData.TotalData={1} EphemeralData.TotalNodes={2} EphemeralData.TotalData={3}", this.backend.Factory.TotalNodes, this.backend.Factory.TotalData, this.backend.EphemeralFactory.TotalNodes, this.backend.EphemeralFactory.TotalData); } result = Code.Ok; break; } case "dumpnodes": { bool scanEphemerals = pieces.Length > 2 && string.Equals(pieces[2], "scanephemerals", StringComparison.InvariantCultureIgnoreCase); content = this.backend.Factory.DumpAllNodesForDebug(); if (scanEphemerals) { lock (this.SyncObject) { content = content + Environment.NewLine + "---Eph:" + Environment.NewLine + this.backend.ScanForEphemeral() + Environment.NewLine + "---"; } } result = Code.Ok; break; } case "getverbositylevel": { content = "verbosity level is " + this.backend.GetVerbosityLevel(); result = Code.Ok; break; } case "setverbositylevel": { int vlev = int.Parse(pieces[2]); content = string.Format("verbosity level was {0} now is set to {1}", this.backend.GetVerbosityLevel(), vlev); this.backend.SetupVerbosityLevel(vlev); result = Code.Ok; break; } case "cleanuprsl": { RingMasterThreadPool.Instance.QueueUserWorkItem(_ => { Thread.Sleep(10000); Trace.TraceWarning("This instance will wipe its state {0} now", this.backend.GetType().Name); this.backend.Factory.WipeAllDataAndShutdown(); Trace.TraceWarning("This instance will die now"); Environment.Exit(1); }); content = "cleaning up RSL folder and restarting"; result = Code.Ok; break; } case "restart": { uint waitMS = 10000; if (pieces.Length > 2) { if (!uint.TryParse(pieces[2], out waitMS)) { waitMS = 10000; } } // 2 minutes max wait waitMS = Math.Min(waitMS, 120000); RingMasterThreadPool.Instance.QueueUserWorkItem(_ => { Thread.Sleep((int)waitMS); Trace.TraceInformation("This instance will stop {0} now", this.backend.GetType().Name); this.backend.Stop(); Thread.Sleep(2000); Trace.TraceInformation("This instance will restart {0} now", this.backend.GetType().Name); this.backend.Start(CancellationToken.None); }); content = "restarted in " + waitMS + " miliseconds"; result = Code.Ok; break; } case "die": { RingMasterThreadPool.Instance.QueueUserWorkItem(_ => { Thread.Sleep(10000); Trace.TraceWarning("This instance will die now"); Environment.Exit(1); }); if (pieces.Length > 2) { this.backend.FailoverInto(pieces[2]); } content = "die in 10 seconds"; result = Code.Ok; break; } } } return(result); }
/// <summary> /// Initializes a new instance of the <see cref="LoopbackRingMaster"/> class. /// </summary> /// <param name="backend">Backend core</param> /// <param name="readOnlyInterfaceRequiresLocks">Whether readonly interface requires locks</param> public LoopbackRingMaster(RingMasterBackendCore backend, bool readOnlyInterfaceRequiresLocks = true) : this(backend, readOnlyInterfaceRequiresLocks, true) { }