Bug #2164
Server fails to start a Progress app when persistence disabled
100%
Related issues
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
BufferManagerwhich is set when itsinitializemethod is invoked, which is later checked in calls toget, so that a reasonable/understandable error can be raised if theBufferManageris 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
- File hc_upd20130903a.zip added
- Status changed from WIP to Review
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
- File hc_upd20130921a.zip added
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