Project

General

Profile

Feature #6479

pending DISCONNECT and static buffers

Added by Greg Shah almost 2 years ago. Updated almost 2 years ago.

Status:
New
Priority:
Normal
Assignee:
-
Target version:
-
Start date:
Due date:
% Done:

0%

billable:
No
vendor_id:
GCD
version:

6479_patch1.txt Magnifier (6.58 KB) Constantin Asofiei, 05/31/2022 03:23 PM

History

#1 Updated by Greg Shah almost 2 years ago

From #6277-162, by Constantin:

But, the weird thing happens: for a .cls, if the database is connected when the legacy class was loaded (as this is done only once), accessing a property getter which is using buffers in this disconnected database will no longer raise a STOP condition (this is what FWD does, could not duplicate), and will actually lookup the record, even if NUM-DBS and LDBNAME reports no connected database!

FWD currently does this, while in the static property getter:

StopConditionException.<init>(String) line: 98    
ErrorManager.postprocessOnAbend(String[]) line: 3445    
ErrorManager.showErrorAndAbend(int[], String[]) line: 1362    
RecordBuffer.getDatabase() line: 9605    
RecordBuffer.initialize() line: 6876    

It doesn't make any sense, somehow OE keeps the connection open in the static class state... and a DISCONNECT does not affect it.

An example is this:

class oo.Foo:
  def static buffer bbook for book.
  def public static property p1 as int get:
  do on error undo, leave:
     find first bbook no-lock where bbook.book-title = m1().
     end.
     message avail(bbook) num-dbs ldbname(1).
     return 12345.
  end.

  method public static char m1():
      return string(now).
  end.
end.

if you do this:


connect c:\p2j_Test\p2j_test.
message oo.foo:p1.  // database connected, record not found
disconnect "p2j_test".
message oo.foo:p1.  // database disconnected (num-dbs = 0, ldbname(1) = ?), record not found

A question: why do we raise a STOP condition in RecordBuffer.initialize if the database is not connected?

#2 Updated by Greg Shah almost 2 years ago

From #6277-163, by Constantin:

Found the root cause: https://knowledgebase.progress.com/articles/Article/P141997

A DISCONNECT must be set to 'pending' if there are buffers defined as static class members, attached to that database... and as classes can't ever discard static buffers, the DISCONNECT will never execute. But NUM-DBS and LDBNAME function will be affected, will no longer report this 'disconnected but actually not' database.

#3 Updated by Constantin Asofiei almost 2 years ago

The only solution is to mark any buffer defined as a static class member or in a static method as 'static', at RecordBuffer.define. There is no way at runtime to find the correct method on the stack (as the signature is missing).

The current patch is attached.

I need help with ConnectionManager, the proper place to cleanup the NUM-DBS and LDBNAME, but keep the database connected behind the scene.

#5 Updated by Constantin Asofiei almost 2 years ago

The issue is not just about static buffers - we need a reference counter, as the pending disconnect is possible with persistent programs, too.

So, we have a program like this:

procedure proc0.
   find first book no-lock.
   message avail(book) num-dbs ldbname(1).
end.

and another program like this:

connect c:\p2j_Test\p2j_Test.
def var h as handle.
run a.p persistent set h.
// this 'binds' the buffer to the database
run proc0 in h.
// disconnect is marked as pending
disconnect "p2j_test".
// database still connected
run proc0 in h.
// database gets disconnected, as the buffer gets deleted
delete object h.
// this RUN fails because the a.p's RCODE-INFO:DATABASE-REFERENCES dbs are no longer connected 
run a.p persistent set h.

#6 Updated by Constantin Asofiei almost 2 years ago

And considering that static buffers are associated with a legacy class instance (which is the equivalent of a persistent program), and also the class instances which are the same as a persistent program, I need to remove the 'static' conversion flag for the buffer, and work only at runtime - use the reference counter to track buffers bound to a database.

Eric/Ovidiu: how can I properly get the persist.Database from a RecordBuffer's 'ldbOrAlias' parameter? The reference counter will be in a map, with Database as key. Or better said, is BufferManager.activeDatabases actually tracking buffers?

#7 Updated by Constantin Asofiei almost 2 years ago

The latest patch is this:

### Eclipse Workspace Patch 1.0
#P p2j6129a
Index: src/com/goldencode/p2j/persist/ConnectionManager.java
===================================================================
--- src/com/goldencode/p2j/persist/ConnectionManager.java    (revision 3650)
+++ src/com/goldencode/p2j/persist/ConnectionManager.java    (working copy)
@@ -2861,13 +2861,18 @@
     */
    private void disconnectImmediately(Database database, boolean sessionCleanup)
    {
-      if (sessionCleanup)
+      String ldbName = ldbMap.get(database);
+      if (ldbName != null)
       {
-         String ldbName = ldbMap.get(database);
-         if (ldbName != null)
+         if (sessionCleanup)
          {
             removeConnected(ldbName, sessionCleanup);
          }
+         else if (BufferManager.get().databaseInUse(database))
+         {
+            // do nothing
+            return;
+         }
       }

       if (!database.isLocal())
@@ -3645,6 +3650,12 @@
          throw new PersistenceException("DISCONNECT:  Database " + ldbName + " is not open or unknown", 1009);
       }

+      if (BufferManager.get().databaseInUse(getDatabase(ldb)))
+      {
+         // do nothing
+         return;
+      }
+      
       // remove the database from the connected map whether or not the disconnect can occur immediately
       Database database = removeConnected(ldb, false);

Index: src/com/goldencode/p2j/persist/BufferManager.java
===================================================================
--- src/com/goldencode/p2j/persist/BufferManager.java    (revision 3650)
+++ src/com/goldencode/p2j/persist/BufferManager.java    (working copy)
@@ -2627,6 +2627,13 @@
       }
    }

+   boolean databaseInUse(Database database)
+   {
+      MutableInteger val = activeDatabases.get(database);
+      
+      return val != null && val.get() > 0;
+   }
+   
    /**
     * A buffer was initialized. If it was previously stored as an uninitialized, open buffer, move it into
     * the active, open buffers list. Perform special handling for persistent procedure buffers.

I haven't done standalone testcases, and #6277 didn't show that this feature is used there.

In any case, this needs to be fixed.

#8 Updated by Ovidiu Maxiniuc almost 2 years ago

The idea is good. I only see one problem: the activeDatabases of BufferManager tracks databases which have at least an active RecordBuffer, regardless it is static or not. If only the static buffers must checked then we could add supplementary information in activeDatabases, to know whether the buffers from current set are declared static.

#9 Updated by Constantin Asofiei almost 2 years ago

My understanding is that a database can't be disconnected if there is at least a buffer attached to it - static or dynamic, doesn't matter. Although some more testing is required.

This is not a priority for #6277, the issue was found when investigating another problem, for which in the end the root cause was something else.

Also available in: Atom PDF