StratisBitcoinFullNode
StratisBitcoinFullNode copied to clipboard
Create PID file with the process Id when starting the full node
The PID file should contain the FullNode Process ID.
PID file life-cycle - happy path
- Start the FullNode - the PID file with the Process ID is created
- Stop the FullNode - the PID file is removed
PID file life-cycle - extended scenario
- The FullNode should check if the PID file already exists
- If the PID file exists the FullNode should check if the process with Process Id from the PID file is running
- If the process is not running the FullNode should remove the PID file and proceed with the Normal startup
- If the process is running the FullNode should stop and print out a message advising user that the Node is already started using this data directory
- If the PID file doesn't exist the FullNode should proceed with the Normal startup
- If the PID file exists the FullNode should check if the process with Process Id from the PID file is running
Thanks @maciejzaleski Ref https://github.com/stratisproject/StratisBitcoinFullNode/pull/1410
Ah I see this is so that the data folder is marked as in processes.
Yes fair enough another option is to open a file and keep it open (open lock) until the node is shutdown. However pid might be better, we also need to make sure it works the same in linux/os
Does it matter that an unrelated (or related) process with the same PID as stored in the file may be created at some later time. It may cause the above logic to fail on rare occasions? Also the logic should not rely on clean-up (removal of the file) happening.
Perhaps this could be handled as a method on the DataFolder class:
/// <summary>
/// Determines if the folder is available to use.
/// </summary>
/// <returns></returns>
public bool FolderAvailable()
{
int processId = Process.GetCurrentProcess().Id;
var processStartTime = Process.GetCurrentProcess().StartTime.ToString();
var filePath = Path.Combine(this.RootPath, "pid");
if (File.Exists(filePath))
{
string[] savedID = File.ReadAllText(filePath).Split('|');
if (savedID.Length == 2 && int.TryParse(savedID[0], out var savedPid))
{
// If the stored PID matches the current process then the folder is available.
if (savedPid == processId && savedID[1] == processStartTime)
return true;
// Check if the previouly recorded process (by id and start time) is still running.
try
{
Process process = Process.GetProcessById(savedPid);
if (process != null && process.StartTime.ToString() == savedID[1])
return false;
}
catch (ArgumentException) {
// Process is not running.
}
}
File.Delete(filePath);
}
File.WriteAllText(filePath, $"{processId}|{processStartTime}");
return true;
}
@quantumagi this looks good to me, it should probably go in the BaseFeature.
Was thinking of this - is it something we would want to do?:
public static async Task MainAsync(string[] args)
{
try
{
NodeSettings nodeSettings = new NodeSettings(protocolVersion: ProtocolVersion.ALT_PROTOCOL_VERSION, args: args);
if (!nodeSettings.DataFolder.FolderAvailable())
throw new Exception("The Data Folder is in use");
// NOTES: running BTC and STRAT side by side is not possible yet as the flags for serialization are static
var node = new FullNodeBuilder()
.UseNodeSettings(nodeSettings)
.UseBlockStore()
.UsePosConsensus()
.UseMempool()
.UseWallet()
.AddPowPosMining()
.UseApi()
.AddRPC()
.Build();
if (node != null)
await node.RunAsync();
}
catch (Exception ex)
{
Console.WriteLine("There was a problem initializing the node. Details: '{0}'", ex.Message);
}
}
Probably not, it should happen in side the node itself so the logic is applied to every node.
It could go inside the NodeSettings perhaps?
Ah no also a bad idea, it should go in the base feature.