简体   繁体   English

Delphi Direct Oracle Access,DOA 4.0.7.1,TOracleEvent.Stop挂起,如何停止它?

[英]Delphi Direct Oracle Access, DOA 4.0.7.1, TOracleEvent.Stop Hangs, How do I stop it?

FOLLOW-UP (Apr 9, 2014): I cannot repeat this issue after replacing DOA 4.0.7.1 with DOA 4.1.1. 后续报道(2014年4月9日):将DOA 4.0.7.1替换为DOA 4.1.1后,我无法重复此问题。 My question is deprecated! 我的问题已被弃用! However, I am still interested in receiving feedback from anyone that has implemented TOracleEvent in production software. 但是,我仍然希望收到任何在生产软件中实现TOracleEvent的人的反馈。


Original Question: 原始问题:
How do I reliably implement the TOracleEvent for DBMS_ALERTs in Delphi 6 using DOA 4.0.7? 如何使用DOA 4.0.7在Delphi 6中可靠地为DBMS_ALERTs实现TOracleEvent?

or 要么

How do I work around the TOracleEvent.Stop method lockup without hacking the DOA source code? 如何解决TOracleEvent.Stop方法锁定问题而又不破坏DOA源代码?

If possible, I would prefer either a fix or a work around that does not need to modify the original AllRoundAutomations DOA source code. 如果可能的话,我宁愿不需要修改原始AllRoundAutomations DOA源代码的修复程序或解决方法。

I am working with legacy Delphi 6 and AllRoundAutomations Direct Oracle Access DOA 4.0.7. 我正在使用旧版Delphi 6和AllRoundAutomations Direct Oracle Access DOA 4.0.7。 I have hit a show stopper where the DOA TOracleEvent instance cannot be stopped and freed, so the application's process cannot terminate itself. 我碰到了一个显示停止器,其中DOA TOracleEvent实例无法停止和释放,因此应用程序的进程无法自行终止。

My understanding from the DOA documentation is that I should call the myOracleEvent.Stop() to clean up the TOracleEvent instance. 我从DOA文档中了解到,我应该调用myOracleEvent.Stop()来清理TOracleEvent实例。 However, if I execute the Stop() method, my application will "Hang" at that method call. 但是,如果我执行Stop()方法,我的应用程序将在该方法调用处“挂起”。 If I don't call Stop(), my application will close, but its Process will stay alive indefinitely until I kill it with taskmanager or other means. 如果我不调用Stop(),则我的应用程序将关闭,但它的进程将无限期保持活动状态,直到我用taskmanager或其他方式将其杀死。

There are 2 types of signals that the TOracleEvent can manage: dbms_alert and pipes. TOracleEvent可以管理两种信号:dbms_alert和管道。 I am using the dbms_alert signal to enable multiple receivers. 我正在使用dbms_alert信号来启用多个接收器。

A search on this issue finds that a few others have experienced this problem, in the AllRoundAutomations forum, but I have not found any Answers. 在此问题上进行的搜索发现,在AllRoundAutomations论坛中,其他一些人也遇到了此问题,但是我没有找到任何答案。

I do not think this is an Oracle server side issue because the Application's TOracleEvent process is causing the blockage. 我认为这不是Oracle服务器端的问题,因为应用程序的TOracleEvent进程引起了阻塞。 That is, the TOracleEvent instance is not "waiting" for anything, it is just hung. 也就是说,TOracleEvent实例没有“等待”任何东西,它只是挂起了。 It is not released if I kill the "hanging" Oracle sessions server side. 如果我杀死了“挂起的” Oracle会话服务器端,它不会被释放。 Of course, the issue could be that my program does not configure the TOracleEvent properties properly or clean it up properly for disposal. 当然,问题可能出在我的程序没有正确配置TOracleEvent属性或正确清理它以进行处理。

The following is Delphi-6 code for a test case state machine that isolates the problem of the DOA.TOracleEvent.Stop() method freezing. 以下是用于测试用例状态机的Delphi-6代码,该代码隔离了DOA.TOracleEvent.Stop()方法冻结的问题。 If TOracleEvent.Stop() is fixed, then the state machine should display results similar to that shown after the code below. 如果TOracleEvent.Stop()是固定的,则状态机应显示与下面的代码类似的结果。 If not fixed, the Stop() method will freeze when called. 如果不固定,则调用Stop()方法时将冻结。 I was not able to get any small test applications to work reliably without fixing (hacking) the DOA source code. 如果不修复(破解)DOA源代码,我将无法使任何小型测试应用程序可靠运行。

implementation
{$R *.dfm}

const
  ALERT_NAME__STACKOVERFLOW = 'STACKOVERFLOW';

/// This is the TOracleEvent.OnEvent handler that listens for DBMS_ALERT Signals.
procedure TForm1.OracleEvent1Event(Sender: TOracleEvent; const ObjectName: String; const Info: Variant);
var
  ii: integer;
begin
  Memo1.Lines.Add('*> Oracle Event Received!');
  Memo1.Lines.Add('   DBMS_ALERT.SIGNAL name = ' + ObjectName);
  if VarIsArray(Info) then begin
    for ii := 0 to VarArrayHighBound(Info, 1) do begin
      Memo1.Lines.Add('     Message= ' + Info[ii]);
    end;
  end;
  Memo1.Refresh();
end;

procedure TForm1.btnRunStateMachineClick(Sender: TObject);
begin
  // Button clicked to clear the display and start the state machine timer.
  AppState := 0;
  Memo1.Lines.Clear();
  Timer1.Enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Time Interval State Machine that steps through the test sequence. 

  // Stop the Timer for Failsafe
  Timer1.Enabled := false;

  if (AppState = 0) then begin
      try
        Memo1.Lines.Add('==========================');
        Memo1.Lines.Add('Connecting OracleSession2 to DB ...');
        // Check for, Close, and Destroy previous Sessions
        if (Assigned(OracleSession2) and OracleSession2.Connected) then begin
          // Close the Connection
          OracleSession2.LogOff();
          OracleSession2.Connected := false;
          OracleSession2.Free();
          OracleSession2 := nil;
        end;
        if (not Assigned(OracleSession2)) then begin
          OracleSession2 := TOracleSession.Create(self);
        end;
        // Configure Session #2 for general purpose routines
        OracleSession2.LogonDatabase := 'STACKOVERFLOW_DB';
        OracleSession2.LogonPassword := 'drowssap';
        OracleSession2.LogonUserName := 'answer';
        OracleSession2.Pooling := spInternal;
        OracleSession2.ThreadSafe := true;
        OracleSession2.Connected := true;
        Memo1.Lines[Memo1.Lines.Count - 1] := 'DB OracleSession2 Connected!';
        Inc(AppState);
      except
        on ex0: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex0.Message);
        end;
      end;
    end

  else if (AppState = 1) then begin
      try
        Memo1.Lines.Add('Connecting OracleSession1 to DB ...');
        if (Assigned(OracleSession1) and OracleSession1.Connected) then begin
          OracleSession1.LogOff();
          OracleSession1.Connected := false;
          OracleSession1.Free();
          OracleSession1 := nil;
        end;
        if (not Assigned(OracleSession1)) then begin
          OracleSession1 := TOracleSession.Create(self);
        end;

        // Configure Session #1 for the OracleEvent Handler
        OracleSession1.LogonDatabase := 'STACKOVERFLOW_DB';
        OracleSession1.LogonPassword := 'drowssap';
        OracleSession1.LogonUserName := 'answer';
        OracleSession1.Pooling := spInternal;
        OracleSession1.ThreadSafe := true;
        OracleSession1.Connected := true;
        Memo1.Lines[Memo1.Lines.Count - 1] := 'DB OracleSession1 Connected!';
        Inc(AppState);
      except
        on ex1: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex1.Message);
        end;
      end;
    end

  else if (AppState = 2) then begin
      try
        Memo1.Lines.Add('Configuring the Oracle Event Handler ...');
        if (not OracleEvent1.Started) then begin
          if (OracleEvent1.Session = nil) then begin
            OracleEvent1.Session := OracleSession1;
          end;
          OracleEvent1.KeepConnection := false;
          OracleEvent1.Synchronized := true;
          OracleEvent1.TimeOut := 1;
          OracleEvent1.ObjectNames := ALERT_NAME__STACKOVERFLOW;
          OracleEvent1.Start();
          Memo1.Lines[Memo1.Lines.Count -1] := 'Oracle Event Handler Started!';
        end;
        Inc(AppState);
      except
        on ex2: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex2.Message);
        end;
      end;
    end

  else if ((AppState >= 3) and (AppState <= 7)) then begin
      try
        Memo1.Lines.Add('Sending a DBMS_ALERT Signal to StackOverflow ...');
        try
          OracleSession1.DBMS_Alert.Signal(ALERT_NAME__STACKOVERFLOW, 'Hello StackOverflow! ' + FormatDateTime('HH:nn:ss', now));
          OracleSession1.Commit();
        except
          on ex: Exception do begin
            Memo1.Lines.Add('*> Oracle Event Signal ERRORT!');
            Memo1.Lines.Add(ex.Message);
          end;
        end;
        Inc(AppState);
      except
        on ex3: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex3.Message);
        end;
      end;
    end

  else if (AppState = 8) then begin
      try
        Memo1.Lines.Add('Disconnecting OracleSession2 from DB ...');
        if (Assigned(OracleSession2) and OracleSession2.Connected) then begin
          OracleSession2.LogOff();
          OracleSession2.Connected := false;
          OracleSession2.Free();
          OracleSession2 := nil;
        end;
        Memo1.Lines[Memo1.Lines.Count -1] := 'DB OracleSession2 Disconnected!';
        Inc(AppState);
      except
        on ex4: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex4.Message);
        end;
      end;
    end

  else if (AppState = 9) then begin
      try
        Memo1.Lines.Add('Stopping OracleEvent handler.');
        Memo1.Lines.Add(' * THIS IS WHERE THE HANGUP PROBLEM IS CALLED!');
        OracleEvent1.Stop();  // <<<< Freezes here if TOracleEvent.Stop() is not fixed!
        ////
        Memo1.Lines.Add('OracleEvent Handler Stopped OK!');
        Memo1.Lines.Add(' * If we got here, either there were no events...');
        Memo1.Lines.Add('   or the Bug is Fixed B-)');
        Inc(AppState);
      except
        on ex5: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex5.Message);
        end;
      end;
    end

  else if (AppState = 10) then begin
      try
        Memo1.Lines.Add('Disconnecting OracleSession1 from DB ...');
        if (Assigned(OracleSession1) and OracleSession1.Connected) then begin
          OracleSession1.LogOff();
          OracleSession1.Connected := false;
          OracleSession1.Free();
          OracleSession1 := nil;
        end;
        Memo1.Lines[Memo1.Lines.Count -1] := 'DB OracleSession1 Disconnected!';
        Inc(AppState);
      except
        on ex6: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex6.Message);
        end;
      end;
    end

  else begin
    AppState := 0;
  end;

  Memo1.Refresh();
  Timer1.Enabled := (AppState >= 0);
end;

procedure TForm1.btnStopClick(Sender: TObject);
begin
  // Stop Button Clicked to halt the State Machine
  Timer1.Enabled := false;
  Memo1.Lines.Add('State Machine Stopped.');
  Memo1.Refresh();
end;

/// Other handlers on the TOracleEvent instance
procedure TForm1.OracleEvent1Start(Sender: TOracleEvent);
begin
  Memo1.Lines.Add('*> Oracle Event START!');
end;

procedure TForm1.OracleEvent1Stop(Sender: TOracleEvent);
begin
  Memo1.Lines.Add('*> Oracle Event STOP!');
end;

procedure TForm1.OracleEvent1Error(Sender: TOracleEvent; const Error: Exception);
begin
  Memo1.Lines.Add('*> Oracle Event ERROR!');
  Memo1.Lines.Add('=> ' + Error.Message);
end;

procedure TForm1.OracleEvent1TimeOut(Sender: TOracleEvent; var Continue: Boolean);
begin
  Memo1.Lines.Add('*> Oracle Event TIMEOUT!');
end;

The following is the state machine's output: 以下是状态机的输出:

==========================
The following are the results for successfully running 
the state machine with a DOA.TOracleEvent.Stop() "fixed".
Without fixing DOA.TOracleEvent.Stop(), the state machine will "freeze"
==========================

DB OracleSession2 Connected!
DB OracleSession1 Connected!
Configuring the Oracle Event Handler ...
Oracle Event Handler Started!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:41
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:42
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:43
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:44
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:45
DB OracleSession2 Disconnected!
*> Oracle Event TIMEOUT!
Stopping OracleEvent handler.
 * This is where the Hangup problem gets called!
*> Oracle Event STOP!
OracleEvent Handler Stopped OK!
 * If we got here, either there were no events...
   or the Bug is Fixed B-)
DB OracleSession1 Disconnected!
==========================

The following is a revision of the TOracleEvent.Stop routine from the DOA 4.1.1 source code. 以下是对DOA 4.1.1源代码中的TOracleEvent.Stop例程的修订。 The unchanged bulk DOA code in this routine is replaced by '. 此例程中未更改的批量DOA代码被替换为'。 . .' 。”

This revised procedure is a functional work around (so far) for the TOracleEvent.Stop() call that intermittently causes my applications to hang. 修改过的过程(到目前为止)是TOracleEvent.Stop()调用的功能解决方案,它间歇性地导致我的应用程序挂起。

procedure TOracleEvent.Stop;
var
  bLoggedOff: boolean;
begin
  . . .
  . . .
  // Skip the CriticalSection Enter/Leave toggle.
  // This is causing my application to get stuck here!
  //CriticalSection.Enter;
  //CriticalSection.Leave;
  . . .
  . . .
  // LogOff the duplicate sessions
  if (not KeepConnection) then begin
    bLoggedOff := false;
    // Keep trying the logoff until we actually get Logged Off or disconnected.
    while (not bLoggedOff) do begin
      try
        if ((InternalSession <> nil) and (InternalSession.Connected)) then begin
          InternalSession.LogOff;
        end;
        if ((StopSession <> nil) and (StopSession.Connected)) then begin
          StopSession.LogOff;
        end;
        bLoggedOff := true;
      except
        on exLogoff: Exception do begin
          // This error is typically ORA-24909: Call In Progress, Current Operation Cancelled.
          // Sink this hangover error by sleeping it off
          Sleep(1000);
        end;
      end;
    end;
  end;
end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM