Project

General

Profile

Bug #2164

Server fails to start a Progress app when persistence disabled

Added by Hynek Cihlar almost 13 years ago. Updated almost 13 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Target version:
-
Start date:
08/21/2013
Due date:
% Done:

100%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:
reviewer:
production:
No
env_name:
topics:

hc_upd20130903a.zip (32.5 KB) Hynek Cihlar, 09/03/2013 06:13 PM

hc_upd20130921a.zip (32.4 KB) Hynek Cihlar, 09/20/2013 07:59 PM


Related issues

Related to Database - Bug #2163: Server fails to start when missing DMO index New

History

#1 Updated by Hynek Cihlar almost 13 years ago

When server persistence disabled (directory.xml:persistence/active == FALSE) an attempt to start a converted Progress application (with no model and no database dependency) fails with the following exception in server.log.

Daemon Thread [Conversation [00000009:bogus]] (Suspended (exception NullPointerException))   
    BufferManager.resetState(boolean) line: 733   
    TransactionManager.processResettables(boolean) line: 1720   
    TransactionManager.rollbackWorker(int) line: 1648   
    TransactionManager.rollback(String) line: 1521   
    TransactionManager.rollback() line: 1478   
    TransactionManager.abnormalEnd(Throwable, boolean) line: 3247   
    TransactionManager.abnormalEnd(Throwable) line: 3168   
    BlockManager.topLevelBlock(BlockManager$TransactionType, BlockType, Block) line: 6880   
    BlockManager.externalProcedure(BlockManager$TransactionType, Block) line: 213   
    Primes.execute() line: 29  

Another exception is thrown before the NullPointerException above (unreported, visible only in a debugger):

Daemon Thread [Conversation [00000004:bogus]] (Suspended (exception NoSuchElementException))
    LinkedList<E>.getLast() line: 255
    ScopedDictionary<K,V>.addEntry(boolean, K, V) line: 299
    BufferManager.openScope(RecordBuffer, boolean) line: 1223
    TemporaryBuffer(RecordBuffer).openScope() line: 4783
    TemporaryBuffer.openScope() line: 1890
    RecordBuffer.openScope(DataModelObject...) line: 2002
    Primes$1.body() line: 42
    BlockManager.processBody(Block, OnPhrase[]) line: 6939
BlockManager.topLevelBlock(BlockManager$TransactionType, BlockType, Block) line: 6846
BlockManager.externalProcedure(BlockManager$TransactionType, Block) line: 213
    BlockManager.externalProcedure(Block) line: 195
    Primes.execute() line: 29
    NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
    NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57
    DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
    Method.invoke(Object, Object...) line: 606
ControlFlowOps$ExternalProcedureCaller(ControlFlowOps$InternalEntryCaller).invoke(Object...) line: 4132
    ControlFlowOps.invokeImpl(List<Resolver>, handle, character, boolean, boolean, boolean, boolean, String, Object...) line: 3393
    ControlFlowOps.invoke(handle, character, boolean, boolean, boolean, boolean, String, Object...) line: 2805
    ControlFlowOps.invokeImpl(handle, handle, character, boolean, boolean, boolean, boolean, String, Object...) line: 3106
    ControlFlowOps.invoke(character, Object...) line: 308
    ControlFlowOps.invoke(String, Object...) line: 294
    Ask$1.body() line: 35
    BlockManager.processBody(Block, OnPhrase[]) line: 6939
BlockManager.topLevelBlock(BlockManager$TransactionType, BlockType, Block) line: 6846
BlockManager.externalProcedure(BlockManager$TransactionType, Block) line: 213
    BlockManager.externalProcedure(Block) line: 195
    Ask.execute() line: 15

The workaround is to activate persistence even for applications that have no DB dependency.

#2 Updated by Hynek Cihlar almost 13 years ago

In regard to the NullPointerException - because persistence is off BufferManager is not listed in the context's (TransactionManager.workArea) scopeList. However it registers itself as resettable in its private constructor. BufferManager not being in the scopeList leads to zero size of BufferManager.loadedBuffers. NPE is thrown when BufferManager.resetState is called, where at least one buffer is expected in BufferManager.loadedBuffers.

This seems to be slightly inconsistent runtime setup - persistence is off so BufferManager.initialize is not called (hence no scopeable registration), but still, BufferManager happily registers with the runtime through its private constructor where it registers itself as resettable.

#3 Updated by Eric Faulhaber almost 13 years ago

  • Project changed from Liberty to Database

#4 Updated by Eric Faulhaber almost 13 years ago

The converted primes.p sample program actually does have a persistence dependency; it uses a temp-table, which is why we get into the RecordBuffer.openScope call stack. I'm pretty sure that if you start a program with no persistence dependency with the persistence active flag disabled in the directory, it will work.

IMO, the main problem illustrated above is that when persistence is disabed -- but needed by the program -- it is not at all obvious from the error that occurs.

Two options that come to my mind to deal with this are:
  • warn at server startup if persistence is disabled (regardless of whether the program later uses it); or
  • put a private static boolean flag in BufferManager which is set when its initialize method is invoked, which is later checked in calls to get, so that a reasonable/understandable error can be raised if the BufferManager is used without having been initialized.

The former approach is a bit clumsy, in that it is not cleanly targeted at the problem, but rather is a blanket approach. Plus, it requires one to check the server log after an unexpected crash. The latter approach adds a very small overhead to every call (possibly millions of times) to BufferManager.get for a non-production use case. However, given the fact that a boolean check is such a tiny amount of overhead, it should be negligible. Even though adding overhead for a non-production use case rubs me the wrong way, I intellectually prefer the second approach because it recognizes the error at the point of failure. However, I could live with either approach.

#5 Updated by Hynek Cihlar almost 13 years ago

Eric, you are right. I created the issue under false assumption that primes.p, which I used for testing, has no DB dependency. Running hello_world.p with server persistence disabled works ok.

#6 Updated by Eric Faulhaber almost 13 years ago

Even so, you've identified a problem which has confused a number of people over the years. It's appropriate to open the issue. I think it makes sense for you to fix it as part of the ramp-up.

#7 Updated by Hynek Cihlar almost 13 years ago

  • Start date set to 08/21/2013
  • Status changed from New to WIP

#8 Updated by Hynek Cihlar almost 13 years ago

Isn't the BufferManager.get too low-level place for the check? Wouldn't be better to put it in the entry points (or "API") of the persistence layer - like RecordBuffer.openScope, Persistence public methods and maybe some others? Calling these methods states clearly the caller's intention - use server's persistence capabilities. On the other hand the intention of the BufferManager.get call may be not that clear. I can imagine use cases where the get is called in a unit test, during a serialization process, for lifecycle management, etc. - cases where the check is not desired.

#9 Updated by Eric Faulhaber almost 13 years ago

I recommended BufferManager.get() primarily because I was trying to keep it simple; this is the single point called by the various persistence API entry points and by internal implementation code. I think it would be better to change it in one, common place than in these many places. Considering the purposes of the various areas of the code which call it, I think all are appropriate for this check.

At the moment, we don't have unit tests defined and BufferManager is never serialized. I'm not sure to what you're refering with respect to lifecycle management in this context. For the foreseeable future. BufferManager is only going to be used in the context of a fully operational P2J server process, so I think we're safe with this change. If/when we tackle the issue of unit tests, we may need to revisit this choice, depending upon how those are structured.

If I've missed something specific you've found or if you believe I've misunderstood your point, please elaborate.

#10 Updated by Hynek Cihlar almost 13 years ago

Those examples I gave were theoretical cases which indeed may never happen. I'll put the check in the BufferManager.get as you recommend.

#11 Updated by Hynek Cihlar almost 13 years ago

Please review the changes. The submitted files are based to bzr revision number 10379.

#12 Updated by Hynek Cihlar almost 13 years ago

Also, the code changes don't include automated tests. Should tests be part of the solution for this particular issue?

#13 Updated by Eric Faulhaber almost 13 years ago

  • Assignee set to Hynek Cihlar

The code changes look good, though please remove the left margin space that was inserted on every line after the first of the BufferManager.java file header.

We don't have an automated unit test framework in place. Although it may seem silly for a change this simple, please run it through the automated regression test harness. It will be useful for you to familiarize yourself with the process (and of course it won't get far if startup is broken).

#14 Updated by Hynek Cihlar almost 13 years ago

The changes passed regression test, see shared\clients\timco\majic_test_results\10382_9c2ae9a_20130920_hc.zip.
Fixed the white spaces and javadoc in comments.

#15 Updated by Eric Faulhaber almost 13 years ago

The update looks good to me. Please commit and distribute it, and record the revision number here.

#16 Updated by Hynek Cihlar almost 13 years ago

Committed revision 10387.

#17 Updated by Eric Faulhaber almost 13 years ago

  • Status changed from Review to Closed
  • % Done changed from 0 to 100

Also available in: Atom PDF