private void StartLogRecoverDlg_Load(object sender, EventArgs e) { string strError = ""; try { // 起始位置参数 ServerReplicationStart start = ServerReplicationStart.FromString(this.StartInfo.Start); this.textBox_startDate.Text = start.Date; this.textBox_startIndex.Text = start.Index.ToString(); // 通用启动参数 ServerReplicationParam param = ServerReplicationParam.FromString(this.StartInfo.Param); this.comboBox_recoverLevel.Text = param.RecoverLevel; this.checkBox_clearBefore.Checked = param.ClearFirst; this.checkBox_clearBefore.Checked = param.ContinueWhenError; } catch (Exception ex) { strError = ex.Message; goto ERROR1; } return; ERROR1: MessageBox.Show(this, strError); }
// 保存断点信息,并保存 this.StartInfos void SaveBreakPoint(ServerReplicationStart start, ServerReplicationParam param) { // 写入断点文件 this.App.WriteBatchTaskBreakPointFile(this.Name, start.ToString() + "|||" + param.ToString()); }
// 读出断点信息,和恢复 this.StartInfos // return: // -1 出错 // 0 没有发现断点信息 // 1 成功 int ReadBreakPoint( out ServerReplicationStart start, out ServerReplicationParam param, out string strError) { strError = ""; start = null; param = null; string strText = ""; // 从断点记忆文件中读出信息 // return: // -1 error // 0 file not found // 1 found int nRet = this.App.ReadBatchTaskBreakPointFile(this.DefaultName, out strText, out strError); if (nRet == -1) { return(-1); } if (nRet == 0) { strError = "启动失败。因当前还没有断点信息,请指定为其他方式运行"; return(0); } string strStart = ""; string strParam = ""; StringUtil.ParseTwoPart(strText, "|||", out strParam, out strStart); // 可能会抛出异常 start = ServerReplicationStart.FromString(strParam); if (string.IsNullOrEmpty(start.Date) == true || start.Date.Length != 8) { strError = "start 字符串格式不正确 '" + strParam + "'。文件名部分应为 8 字符"; return(-1); } param = ServerReplicationParam.FromString(strStart); return(1); }
BatchTaskStartInfo BuildStartInfo(ServerReplicationStart start, ServerReplicationParam param) { BatchTaskStartInfo info = new BatchTaskStartInfo(); if (start != null) { info.Start = start.ToString(); } if (param != null) { info.Param = param.ToString(); } return(info); }
// return: // -1 出错 // 0 中断 // 1 完成 int ProcessOperLogs(ServerReplicationStart breakpoint, bool bContinueWhenError, Delegate_saveBreakPoint func_saveBreakPoint, string strStyle, out string strError) { strError = ""; DateTime now = DateTime.Now; string strStartDate = breakpoint.Date; // +":" + breakpoint.Offset.ToString(); string strEndDate = DateTimeUtil.DateTimeToString8(now); List <string> filenames = null; string strWarning = ""; // 根据日期范围,发生日志文件名 // parameters: // strStartDate 起始日期。8字符 // strEndDate 结束日期。8字符 // return: // -1 错误 // 0 成功 int nRet = OperLogLoader.MakeLogFileNames(strStartDate, strEndDate, true, // true, out filenames, out strWarning, out strError); if (nRet == -1) { return(-1); } if (String.IsNullOrEmpty(strWarning) == false) { // 可能有超过当天日期的被舍弃 } #if NO if (filenames.Count > 0 && string.IsNullOrEmpty(strEndRange) == false) { filenames[filenames.Count - 1] = filenames[filenames.Count - 1] + ":" + strEndRange; } if (filenames.Count > 0 && string.IsNullOrEmpty(strStartRange) == false) { filenames[0] = filenames[0] + ":" + strStartRange; } #endif if (filenames.Count > 0 && breakpoint.Index > 0) { filenames[0] = filenames[0] + ":" + breakpoint.Index.ToString() + "-"; } string strTempFileName = ""; strTempFileName = this.App.GetTempFileName("attach"); #if NO LibraryChannel channel = new LibraryChannel(); channel.Timeout = TimeSpan.FromSeconds(30); channel.Url = this.m_strUrl; channel.BeforeLogin += new BeforeLoginEventHandle(Channel_BeforeLogin); #endif LibraryChannel channel = this.GetChannel(); _stop.BeginLoop(); try { DateTime lastSaveTime = DateTime.Now; TimeSpan delta = TimeSpan.FromMinutes(1); OperLogLoader loader = new OperLogLoader(); loader.Channel = channel; loader.Stop = this._stop; // loader.estimate = estimate; loader.Dates = filenames; loader.Level = 0; // 0 完整级别 loader.ReplicationLevel = true; loader.AutoCache = false; loader.CacheDir = ""; // loader.Filter = "borrow,return,setReaderInfo,setBiblioInfo,setEntity,setOrder,setIssue,setComment,amerce,passgate,getRes"; loader.LogType = LogType.OperLog; foreach (OperLogItem item in loader) { if (this.Stopped) { strError = "用户中断"; return(0); } string date = item.Date; // 处理 // this.AppendResultText("--" + date + "\r\n"); this.SetProgressText(date + ":" + item.Index.ToString()); Stream attachment = null; try { if (item.AttachmentLength != 0) { // return: // -1 出错 // 0 没有找到日志记录 // >0 附件总长度 long lRet = loader.DownloadAttachment(item, strTempFileName, out strError); if (lRet == -1 || lRet == 0) { strError = "做日志记录 " + item.Date + " " + (item.Index).ToString() + " 时,下载附件部分发生错误:" + strError; this.AppendResultText("*** " + strError + "\r\n"); if (// this.RecoverLevel == RecoverLevel.Logic && bContinueWhenError == false) { return(-1); } goto CONTINUE; } attachment = File.Open(strTempFileName, FileMode.Open); } nRet = this.DoOperLogRecord( this.RecoverLevel, item.Xml, attachment, strStyle, out strError); if (nRet == -1) { strError = "做日志记录 " + item.Date + " " + (item.Index).ToString() + " 时发生错误:" + strError; this.AppendResultText("*** " + strError + "\r\n"); // 2007/6/25 // 如果为纯逻辑恢复(并且 bContinueWhenError 为 false),遇到错误就停下来。这便于进行测试。 // 若不想停下来,可以选择“逻辑+快照”型,或者设置 bContinueWhenError 为 true if (// this.RecoverLevel == RecoverLevel.Logic && bContinueWhenError == false) { return(-1); } } } finally { if (attachment != null) { attachment.Close(); attachment = null; } } CONTINUE: breakpoint.Date = date; breakpoint.Index = item.Index + 1; // 中途记忆断点 if (DateTime.Now - lastSaveTime >= delta) { if (func_saveBreakPoint != null) { func_saveBreakPoint(breakpoint); } lastSaveTime = DateTime.Now; } } return(1); } catch (InterruptException) { strError = "用户中断"; return(0); } catch (ChannelException ex) { strError = "处理过程出错: " + ex.Message; return(-1); } catch (Exception ex) { strError = "ProcessOperLogs() 出现异常: " + ExceptionUtil.GetDebugText(ex); return(-1); } finally { if (File.Exists(strTempFileName)) { File.Delete(strTempFileName); } _stop.EndLoop(); #if NO channel.BeforeLogin -= new BeforeLoginEventHandle(Channel_BeforeLogin); channel.Close(); #endif this.ReturnChannel(channel); } }
// 一次操作循环 public override void Worker() { // 系统挂起的时候,不运行本线程 if (this.App.ContainsHangup("LogRecover") == true) { return; } if (this.App.PauseBatchTask == true) { return; } this.Loop = true; // 2017/10/16 BatchTaskStartInfo startinfo = this.StartInfo; if (startinfo == null) { startinfo = new BatchTaskStartInfo(); // 按照缺省值来 } string strError = ""; int nRet = 0; ServerReplicationStart start = ServerReplicationStart.FromString(startinfo.Start); ServerReplicationParam param = ServerReplicationParam.FromString(startinfo.Param); #if NO long lStartIndex = 0; // 开始位置 string strStartFileName = ""; // 开始文件名 nRet = ParseReplicationStart(startinfo.Start, out lStartIndex, out strStartFileName, out strError); if (nRet == -1) { this.AppendResultText("启动失败: " + strError + "\r\n"); return; } // string strRecoverLevel = ""; bool bClearFirst = false; bool bContinueWhenError = false; nRet = ParseReplicationParam(startinfo.Param, out strRecoverLevel, out bClearFirst, out bContinueWhenError, out strError); if (nRet == -1) { this.AppendResultText("启动失败: " + strError + "\r\n"); return; } #endif this.App.WriteErrorLog(this.Name + " 任务启动。"); // 获得源 dp2library 服务器配置信息 { nRet = GetSourceServerCfg(out strError); if (nRet == -1) { goto ERROR1; } nRet = CheckUID(out strError); if (nRet == -1) { goto ERROR1; } } #if NO // 当为容错恢复级别时,检查当前全部读者库的检索点是否符合要求 if (this.RecoverLevel == LibraryServer.RecoverLevel.Robust) { // 检查全部读者库的检索途径,看是否满足都有“所借册条码号”这个检索途径的这个条件 // return: // -1 出错 // 0 不满足 // 1 满足 nRet = this.App.DetectReaderDbFroms(out strError); if (nRet == -1) { this.AppendResultText("检查读者库检索点时发生错误: " + strError + "\r\n"); return; } if (nRet == 0) { this.AppendResultText("在容错恢复级别下,当前读者库中有部分或全部读者库缺乏“所借册条码号”检索点,无法进行日志恢复。请按照日志恢复要求,刷新所有读者库的检索点配置,然后再进行日志恢复\r\n"); return; } } #endif // TODO: 检查当前是否有 重建检索点 的后台任务正在运行,或者还有没有运行完的部分。 // 要求重建检索点的任务运行完以后才能执行日志恢复任务 #if NO if (bClearFirst == true) { nRet = this.App.ClearAllDbs(this.RmsChannels, out strError); if (nRet == -1) { this.AppendResultText("清除全部数据库记录时发生错误: " + strError + "\r\n"); return; } } #endif // 进行处理 // BreakPointInfo breakpoint = null; #if NO if (string.IsNullOrEmpty(start.StartFileName) == false && start.StartFileName != "continue") { breakpoint = new BreakPointInfo(start.StartFileName.Substring(0, 8), start.StartIndex); } #endif this.AppendResultText("*********\r\n"); if (start.Date == "continue" || string.IsNullOrEmpty(start.Date)) { // 按照断点信息处理 this.AppendResultText("从上次断点位置继续\r\n"); // return: // -1 出错 // 0 没有发现断点信息 // 1 成功 nRet = ReadBreakPoint(out start, out param, out strError); if (nRet == -1) { goto ERROR1; } if (nRet == 0) { // return; goto ERROR1; } // 此后返回前可以用 start 写入断点文件了 } else { // 先从远端复制整个数据库,然后从开始复制时的日志末尾进行同步 this.AppendResultText("指定的数据库\r\n"); // 采纳先前创建好的复制并继续的断点信息 } Debug.Assert(start != null, ""); this.AppendResultText("计划进行的处理:\r\n---\r\n" + start.GetSummary() + "\r\n---\r\n\r\n"); // TODO: 处理中途可以定期保存断点文件,这样可以在掉电等情况下也能尽量保证后续从断点位置附近开始处理 // return: // -1 出错 // 0 中断 // 1 完成 nRet = ProcessOperLogs(start, param.ContinueWhenError, (s) => { SaveBreakPoint(s, param); }, param == null ? "" : param.Style, out strError); if (nRet == -1 || nRet == 0) { // 保存断点文件 // 迫使后面循环处理的时候,从断点位置继续 goto ERROR2; } #if NO bool bStart = false; if (String.IsNullOrEmpty(strStartFileName) == true) { // 做所有文件 bStart = true; } // 列出所有日志文件 DirectoryInfo di = new DirectoryInfo(this.App.OperLog.Directory); FileInfo[] fis = di.GetFiles("*.log"); // BUG!!! 以前缺乏排序。2008/2/1 Array.Sort(fis, new FileInfoCompare()); for (int i = 0; i < fis.Length; i++) { if (this.Stopped == true) { break; } string strFileName = fis[i].Name; this.AppendResultText("检查文件 " + strFileName + "\r\n"); if (bStart == false) { // 从特定文件开始做 if (string.CompareOrdinal(strStartFileName, strFileName) <= 0) // 2015/9/12 从等号修改为 Compare { bStart = true; if (lStartIndex < 0) { lStartIndex = 0; } // lStartIndex = Convert.ToInt64(startinfo.Param); } } if (bStart == true) { nRet = DoOneLogFile(strFileName, lStartIndex, bContinueWhenError, out strError); if (nRet == -1) { goto ERROR1; } lStartIndex = 0; // 第一个文件以后的文件就全做了 } } #endif this.AppendResultText("循环结束\r\n"); this.App.WriteErrorLog("日志恢复 任务结束。"); // 保存断点文件 SaveBreakPoint(start, param); this.StartInfo = BuildStartInfo(null, param); // 迫使后面循环处理的时候,从断点位置继续 return; ERROR1: this.AppendResultText(strError + "\r\n任务因出错而中断。\r\n"); return; ERROR2: // 保存断点文件 SaveBreakPoint(start, param); this.StartInfo = BuildStartInfo(null, param); // 迫使后面循环处理的时候,从断点位置继续 this.AppendResultText(strError + "\r\n任务因出错而中断。\r\n"); return; }
private void button_OK_Click(object sender, EventArgs e) { string strError = ""; if (this.comboBox_recoverLevel.Visible == true && this.comboBox_recoverLevel.Text == "") { strError = "尚未指定 恢复级别"; goto ERROR1; } if (string.IsNullOrEmpty(this.textBox_startDate.Text) == false && this.textBox_startDate.Text.Length != 8) { strError = "起始日期 应为 8 字符形态"; goto ERROR1; } ServerReplicationStart start = new ServerReplicationStart(); start.Date = this.textBox_startDate.Text; { long index = 0; if (this.textBox_startIndex.Text != "") { try { index = Convert.ToInt64(this.textBox_startIndex.Text); } catch (Exception) { strError = "记录序号 '" + this.textBox_startIndex.Text + "' 必须为纯数字"; goto ERROR1; } } start.Index = index; } this.StartInfo.Start = start.ToString(); // 通用启动参数 string strRecoverLevel = this.comboBox_recoverLevel.Text; int nRet = strRecoverLevel.IndexOf('('); if (nRet != -1) { strRecoverLevel = strRecoverLevel.Substring(0, nRet).Trim(); } ServerReplicationParam param = new ServerReplicationParam(); param.RecoverLevel = strRecoverLevel; param.ClearFirst = this.checkBox_clearBefore.Checked; param.ContinueWhenError = this.checkBox_continueWhenError.Checked; this.StartInfo.Param = param.ToString(); this.DialogResult = DialogResult.OK; this.Close(); return; ERROR1: MessageBox.Show(this, strError); }