public static bool TryStartSqlProcess(HttpContext context, string exe, string arguments, out MySqlProcess process) { lock (_lockObj) { process = _process; if (process != null) { try { if (!process.HasExited) { context.Response.StatusCode = 409; context.Response.Write(string.Format("The process {0}:{1} is still running!", process.Id, process.ProcessName)); context.Response.End(); return(false); } } catch (Exception) { } _process = null; } _process = MySqlProcess.Start(exe, arguments); process = _process; return(true); } }
private Exception TerminateProcess(MySqlProcess process, Exception ex) { process.Kill(); Trace("{0}:{1} killed.", process.ProcessName, process.Id); return(new InvalidOperationException(String.Format("Wait for process {0}:{1} failed with {2}", process.ProcessName, process.Id, ex), ex)); }
private async Task HandleResponse(HttpContext context) { var process = _process; if (process != null) { if (!process.HasExited) { try { if (process.StartTime.Add(Utils.MySqlTimeout) < DateTime.UtcNow) { throw new TimeoutException(String.Format("Execute sql process {0}:{1} has exceeded {2} timeout", process.ProcessName, process.Id, Utils.MySqlTimeout)); } } catch (Exception ex) { _process = null; throw TerminateProcess(process, ex); } // return location var location = new Uri(context.Request.Url, Utils.GetPath("{0}/dump/status")); context.Response.Headers["Location"] = location.AbsoluteUri; context.Response.StatusCode = 202; return; } Trace("{0}:{1} exited with {2}.", process.ProcessName, process.Id, process.ExitCode); if (process.ExitCode != 0) { throw new InvalidOperationException(String.Format("{0}:{1} exit with {2}! {3}", process.ProcessName, process.Id, process.ExitCode, process.Output)); } } _process = null; var prefix = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME"); if (String.IsNullOrEmpty(prefix)) { prefix = "mysqldump"; } var fileName = String.Format("{0}-{1:yy-MM-dd-HH-mm-ss}.sql", prefix, DateTime.UtcNow); context.Response.ContentType = "text/plain"; context.Response.Headers["Content-Disposition"] = "attachment; filename=" + fileName; context.Response.Cache.SetCacheability(HttpCacheability.NoCache); using (var fs = File.OpenRead(_resultFile)) { await fs.CopyToAsync(context.Response.OutputStream); } context.Response.End(); Utils.SafeExecute(() => File.Delete(_resultFile), _tracer); }
public override async Task ProcessRequestAsync(HttpContext context) { if (context.Request.RawUrl.IndexOf(Utils.GetPath("{0}/dump/status"), StringComparison.OrdinalIgnoreCase) == 0) { await HandleResponse(context); return; } var exe = Path.Combine(BasePath, "bin", "mysqldump.exe"); // --single-transaction aquires a REPEATABLE READ lock for the time of the dump to ensure consistency // http://dev.mysql.com/doc/refman/5.6/en/mysqldump.html#option_mysqldump_single-transaction var arguments = string.Format(@"--single-transaction -u{0} -p{1} -h{2} --port={3} --result-file={4} {5}", UserID, Password, Server, Port, _resultFile, Database); Trace("\"{0}\" {1}", exe, arguments.Replace(Password, "*****")); MySqlProcess process; if (!TryStartSqlProcess(context, exe, arguments, out process)) { return; } var processName = process.ProcessName; var processId = process.Id; _process = process; Trace("{0}:{1} started", processName, processId); if (context.Request.QueryString["async"] == "1") { await HandleResponse(context); return; } try { // wait for exit await process.WaitForExitAsync(Utils.MySqlTimeout); } catch (Exception ex) { _process = null; throw TerminateProcess(process, ex); } // to flush all output process.WaitForExit(); await HandleResponse(context); }
public async override Task ProcessRequestAsync(HttpContext context) { var exe = Path.Combine(BasePath, "bin", "mysql.exe"); var arguments = string.Format(@"-u{0} -p{1} -h{2} --port={3} {4}", UserID, Password, Server, Port, Database); Trace("\"{0}\" {1}", exe, arguments.Replace(Password, "*****")); var process = MySqlProcess.Start(exe, arguments); var processName = process.ProcessName; var processId = process.Id; Trace("{0}:{1} started", processName, processId); Func <CancellationToken, Task> executeSql = async(CancellationToken cancellationToken) => { await context.Request.InputStream.CopyToAsync(process.Input, 4096, cancellationToken); await process.Input.FlushAsync(cancellationToken); var quitCmd = Encoding.UTF8.GetBytes("\n\\q\n"); await process.Input.WriteAsync(quitCmd, 0, quitCmd.Length, cancellationToken); await process.Input.FlushAsync(cancellationToken); if (!cancellationToken.IsCancellationRequested) { // to flush all output process.WaitForExit(); } }; var cts = new CancellationTokenSource(); var tasks = new[] { Task.Delay(Utils.MySqlTimeout, cts.Token), executeSql(cts.Token) }; var task = await Task.WhenAny(tasks); // cancel all pending tasks cts.Cancel(); // first task is Delay. This implies timeout. if (task == tasks[0]) { process.Kill(); Trace("{0}:{1} killed.", processName, processId); throw new TimeoutException(String.Format("Execute sql process {0}:{1} has exceeded {2} timeout", processName, processId, Utils.MySqlTimeout)); } // to ensure successful in executing query and process has exited await task; Trace("{0}:{1} exited with {2}.", processName, processId, process.ExitCode); if (process.ExitCode != 0) { throw new InvalidOperationException(String.Format("{0}:{1} exit with {2}! {3}", processName, processId, process.ExitCode, process.Output)); } context.Response.StatusCode = 200; context.Response.Write(process.Output); context.Response.End(); }