Bug #9032
Reduce the number of exceptions that are being thrown
50%
Related issues
History
#1 Updated by Dănuț Filimon almost 2 years ago
This is a separate issue created based on #7026-314.
From the same note:4. Exceptions
| Class | Count |
|---|---|
| com.goldencode.p2j.NumberedException | 1391373 |
| com.goldencode.p2j.util.ErrorConditionException | 1278987 |
| java.lang.ClassNotFoundException | 71867 |
| java.lang.NoSuchMethodException | 60530 |
| com.goldencode.p2j.util.LegacyErrorException | 59588 |
| java.lang.Exception | 59588 |
| com.goldencode.p2j.util.DeferredLegacyErrorException | 53139 |
| java.lang.NumberFormatException | 34201 |
| java.lang.Throwable | 31124 |
| com.goldencode.p2j.persist.ValidationException | 15554 |
java.lang.ClassNotFoundExceptiontop 11 messages which is really strange:Message Count com.goldencode.p2j.util.FieldReference 5258 java.lang.FieldReference 5258 java.lang.AdaptiveQuery 4270 com.goldencode.p2j.util.AdaptiveQuery 4270 com.goldencode.p2j.util.RandomAccessQuery 3980 java.lang.RandomAccessQuery 3980 java.lang.CompoundQuery 1160 com.goldencode.p2j.util.CompoundQuery 1160 java.lang.QueryConstants 404 com.goldencode.p2j.util.QueryConstants 404 com.goldencode.expr.CE4424 3
java.lang.NoSuchMethodExceptiontop 11 messages:Message Count com.goldencode.p2j.util.character.<init>(com.goldencode.p2j.util.logical) 5145 com.goldencode.p2j.util.TextOps.toUpperCase(com.goldencode.p2j.util.character) 2378 com.goldencode.p2j.util.BlockManager.toUpperCase(com.goldencode.p2j.util.character) 2378 com.goldencode.p2j.persist.AbstractQuery.addComponent(com.goldencode.p2j.persist.RandomAccessQuery, java.lang.Integer, java.lang.Boolean) 1829 com.goldencode.p2j.persist.CompoundQuery.addComponent(com.goldencode.p2j.persist.RandomAccessQuery, java.lang.Integer, java.lang.Boolean) 1829 com.goldencode.p2j.persist.DynamicQuery.addComponent(com.goldencode.p2j.persist.RandomAccessQuery, java.lang.Integer, java.lang.Boolean) 1829 java.lang.Object.addComponent(com.goldencode.p2j.persist.RandomAccessQuery, java.lang.Integer, java.lang.Boolean) 1829 java.lang.Object.addComponent(com.goldencode.p2j.persist.AdaptiveQuery) 1173 com.goldencode.p2j.persist.AbstractQuery.addComponent(com.goldencode.p2j.persist.AdaptiveQuery) 1173 com.goldencode.p2j.persist.CompoundQuery.addComponent(com.goldencode.p2j.persist.AdaptiveQuery) 1173 com.goldencode.p2j.persist.DynamicQuery.addComponent(com.goldencode.p2j.persist.AdaptiveQuery) 1173
The java.lang.ClassNotFoundException looks up classes that do not actually exist, while java.lang.NoSuchMethodException lookup up methods that do not exist.
#2 Updated by Dănuț Filimon almost 2 years ago
- Status changed from New to WIP
- Assignee set to Dănuț Filimon
- % Done changed from 0 to 30
- Avoid looking up methods when the signature include unknown type/null/UnknownType parameters and simply skip to another method which can lookup/determine the type of the parameter and find the method.
importsarray was changes and "java.lang.*" si now at the end because it was not used at all, it can also contain null paths
For staticImports, which is the leading factor of NoSuchMethodException, I am not sure how to start as the KW_IMPORT/STATIC_IMPORT are added at conversion.
Right now I've done a similar test to the one from #7206-314 (10 runs + warmup) and managed to reduce the ClassNotFoundException from 68417 to 53345 and NoSUchMethodException from 53484 to 36435.
I am also looking at other errors produced in this scenario and planning to investigate and check if there should not be thrown (ErrorConditionException - Count Cannot delete a BY-REFERENCE PARAMETER dataset or table in the called procedure. (12327)).
#3 Updated by Ovidiu Maxiniuc almost 2 years ago
Dănuț,
I looked over your changes in r15367, Jast interpreter. I have a few questions here:- in which cases
importscontainsnullvalues? This array is initialized and populate incollectImportPaths(), usingpackageName.substring(). It will never returnnull. - why
null/unknown values are not allowed to be parameters of a constructor/method? I understand that what you did is rather an optimisation, by looking up first the constructor/method withoutnullvalues, which should return quickly using the unique signature. The old resolution is performed only in cases wherenullvalues likely cause ambiguities; - although the above point is clearly an optimization, I do not think it will decrease the
ClassNotFoundException. Maybe justNoSuchMethodExceptions, which are internally caught for advancing to next overloaded method, but they should not be present in the log.
#4 Updated by Dănuț Filimon almost 2 years ago
Ovidiu Maxiniuc wrote:
Dănuț,
I looked over your changes in r15367, Jast interpreter. I have a few questions here:
- in which cases
importscontainsnullvalues? This array is initialized and populate incollectImportPaths(), usingpackageName.substring(). It will never returnnull.
You are right, the import path is actually "null." and not null, the value comes from the following compilation unit Aast:
compilation unit [COMPILE_UNIT]:68719476737 @0:0
com.goldencode.p2j.util.* [KW_IMPORT]:68719476739 @0:0
com.goldencode.p2j.util.BlockManager.* [STATIC_IMPORT]:68719476740 @0:0
DynGenQuery [KW_CLASS]:68719476741 @0:0
[CS_INSTANCE_VARS]:68719476745 @0:0
= [ASSIGN]:68719476811 @0:0
AdaptiveQuery [REFERENCE_DEF]:68719476812 @0:0
AdaptiveQuery [CONSTRUCTOR]:68719476813 @0:0
[CS_INSTANCE_METHODS]:68719476748 @0:0
execute [METHOD_DEF]:68719476750 @0:0
[BLOCK]:68719476751 @0:0
initialize [METHOD_CALL]:68719476816 @0:0
query0 [REFERENCE]:68719476817 @0:0
bittTablePhysical [REFERENCE]:68719476818 @0:0
String [CAST]:68719476819 @0:0
[NULL_LITERAL]:68719476820 @0:0
[NULL_LITERAL]:68719476821 @0:0
bittTablePhysical._rowState asc [STRING]:68719476822 @0:0
WHOLE-INDEX,rowState [STRING]:68719476823 @0:0
com.goldencode.p2j.persist.* [KW_IMPORT]:68719476777 @0:0
null.* [KW_IMPORT]:68719476778 @0:0 <--------------------------------------------- here
com.app.dmo._temp.* [KW_IMPORT]:68719476793 @0:0
com.goldencode.p2j.persist.lock.* [KW_IMPORT]:68719476824 @0:0
I am not talking about the value of the parameter for a method, I am talking about the type of the parameter. Let me clarify these cases:
- why
null/unknown values are not allowed to be parameters of a constructor/method? I understand that what you did is rather an optimisation, by looking up first the constructor/method withoutnullvalues, which should return quickly using the unique signature. The old resolution is performed only in cases wherenullvalues likely cause ambiguities;
- ControlFlowOps: This scenario receives
Object... param, one of the parameter is anew unknown()(assigned to a handle in the external procedure right at the start, not modified further) so this unknown value is passed as a parameter and there is no method that has an unknown type instance in it's signature. Here the first two checks for the public/private method will fail, then it will later go through all the methods and determine that thenew unknown()is actually ahandle. - SymbolResolver: A similar situation, for setters we don't actually know the type so we pass a signature containing a single
UnknownType.classto later determine it's appropriate type. The type can also be null (ExpressionParser.method()callsExpressionParser.generateSignature()which can end up adding a null value because ofVerifier.resolveDataType()- this method has a switch case for the ast type and if the type isNULLit will return of course null). The comments left in theSymbolResolver.introspectFunction()also point out that null matching is done after attempting reflection (I don't think it should attempt it at all in this case). - RuntimeJastInterpreter: This a similar situation to the one from SymbolResolver for null. Let me add that the "java.lang.*" import was moved and it's similar to
staticImportswhich also adds the Math.class at the end, and that I should remove the null check for paths as the "null." was not hit in previous tests (with or without changes). I should look into how the "null.*" ends up as a KW_IMPORT, any tips on this?
- although the above point is clearly an optimization, I do not think it will decrease the
ClassNotFoundException. Maybe justNoSuchMethodExceptions, which are internally caught for advancing to next overloaded method, but they should not be present in the log.
Danut Filimon wrote:
Right now I've done a similar test to the one from #7206-314 (10 runs + warmup) and managed to reduce the ClassNotFoundException from 68417 to 53345 and NoSuchMethodException from 53484 to 36435.
This is attributed entirely to moving the "java.lang.*" import path at the end, although I still have ClassNotFoundException caused by searching classes such as com.goldencode.p2j.util.FieldRefernce, com.goldencode.p2j.util.AdaptiveQuery, com.goldencode.p2j.util.RandomAccessQuery, com.goldencode.p2j.util.CompoundQuery, com.goldencode.p2j.util.QueryConstants which are actually from the persist package.
#5 Updated by Ovidiu Maxiniuc almost 2 years ago
Dănuț Filimon wrote:
Ovidiu Maxiniuc wrote:
You are right, the import path is actually"null."and notnull, the value comes from the following compilation unit Aast:
[...]
This is nonsense. Please find the TRPL which generates this and fix the issue there. We need to eliminate the bad annotation, not to recover after this problem.
I am not talking about the value of the parameter for a method, I am talking about the type of the parameter. Let me clarify these cases:
Of course. The presence of null values makes it impossible to know the type of the parameter on that position.
- ControlFlowOps: This scenario receives
Object... param, one of the parameter is anew unknown()(assigned to a handle in the external procedure right at the start, not modified further) so this unknown value is passed as a parameter and there is no method that has an unknown type instance in it's signature. Here the first two checks for the public/private method will fail, then it will later go through all the methods and determine that thenew unknown()is actually ahandle.
Probably the static context cannot provide the actual type for a _poly method/function/method. At runtime you know it should be a handle, but when conversion runs, the only solution is to use the unknown class.
- RuntimeJastInterpreter: This a similar situation to the one from SymbolResolver for null. Let me add that the "java.lang.*" import was moved and it's similar to
staticImportswhich also adds the Math.class at the end, and that I should remove the null check for paths as the "null." was not hit in previous tests (with or without changes). I should look into how the "null.*" ends up as a KW_IMPORT, any tips on this?
Yes, as noted above. you can breakpoint the places where the import JASTs are created (KW_IMPORT and STATIC_IMPORT).
This is attributed entirely to moving the "java.lang.*" import path at the end, although I still have ClassNotFoundException caused by searching classes such as
com.goldencode.p2j.util.FieldRefernce, com.goldencode.p2j.util.AdaptiveQuery, com.goldencode.p2j.util.RandomAccessQuery, com.goldencode.p2j.util.CompoundQuery, com.goldencode.p2j.util.QueryConstantswhich are actually from the persist package.
OK. I understand. Here is an idea of how this can be avoided. We can keep a static map with hints for the package (maybe better the .class) of the most used class names, like FieldRefernce, AdaptiveQuery, etc. Before iterating the imports we can use this map quickly identify the correct class/package. However, this is a highly heuristic approach and only the name of the classes which we are sure are unique should be hinted. This can actually improve the resolution performance.
#6 Updated by Dănuț Filimon almost 2 years ago
I've identified where the "null.*" is added, it comes from CE3124 (I remember seeing this type of class a lot), the stack trace:
ConverterHelper.createImport(String, boolean, JavaAst, AstSymbolResolver) line: 174 ConverterHelper$Helper.createImport(String) line: 217 CE3124.execute() line: not available [local variables unavailable] Expression.execute() line: 380 Rule.apply(AstSymbolResolver, int) line: 500 Rule.executeActions(AstSymbolResolver, ArrayList<RuleListElement>) line: 751 Rule.coreProcessing(AstSymbolResolver, Object) line: 717 Rule.apply(AstSymbolResolver, int) line: 537 RuleSet(RuleContainer).apply(AstSymbolResolver, int) line: 597 RuleSet.apply(AstSymbolResolver, int) line: 98 RuleSet(RuleContainer).apply(AstSymbolResolver, int) line: 597 RuleSet.apply(AstSymbolResolver, int) line: 98 AstWalker.walk(AstSymbolResolver, Iterator<Aast>) line: 262 AstWalker.walk(AstSymbolResolver, Aast, int) line: 210 PatternEngine.apply(RuleSet, Aast) line: 1692 PatternEngine.processAst(Aast) line: 1578 PatternEngine.run() line: 947 ConversionPool.runTask(ConversionProfile, ConversionPool$Results, Aast...) line: 346 ConversionPool.runTask(ConversionProfile, Aast...) line: 301 DynamicQueryHelper.lambda$parse$10(ProgressAst) line: 523 239296655.exec() line: not available NanoTimer.timer(Operation) line: 131 DynamicQueryHelper.parse(QueryProcessor, ArrayList<Buffer>, String, String, List<Buffer>) line: 523 DynamicQueryHelper.lambda$parseQuery$12(P2JQuery[], DynamicQueryHelper$QueryProcessor, ArrayList, String, String, List) line: 1017 796042721.exec() line: not available NanoTimer.timer(Operation) line: 131 DynamicQueryHelper.parseQuery(ArrayList<Buffer>, String, String, List<Buffer>) line: 1017 QueryWrapper.prepare(character) line: 4478Not sure where this CE3124 comes from?
#7 Updated by Dănuț Filimon almost 2 years ago
The "null.*" comes from rules/convert/buffer_definitions.rules line 703.
#8 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
The "null.*" comes from
rules/convert/buffer_definitions.rules line 703.
This is the problematic rule. In the case where the second action is executed, the pkg does not have any value as the only appearance is only it being declared so it makes sense for it to be null when the getNoteString("pkgname") is also null.
<!-- handle buffer-specific import statement -->
<rule>
isNote("is_dynamic_table") and getNoteBoolean("is_dynamic_table")
<action>pkg = "com.goldencode.p2j.persist.dynamic._temp.*"</action>
<action on="false">pkg = sprintf("%s.*", getNoteString("pkgname"))</action>
</rule>
<action>createImport(pkg)</action>
I should dig further into why is the pkgname null.#9 Updated by Ovidiu Maxiniuc almost 2 years ago
Dănuț Filimon wrote:
[...] Not sure where this CE3124 comes from?
The TRPL code is assembled into Java code. This is one of the classes generated on-the-fly.
The condition field from Rule.apply stack element should give you the exact TRPL rule being executed.
Dănuț Filimon wrote:
The "null.*" comes from
rules/convert/buffer_definitions.rules line 703.This is the problematic rule. In the case where the second action is executed, the
pkgdoes not have any value as the only appearance is only it being declared so it makes sense for it to be null when thegetNoteString("pkgname")is also null.
[...] I should dig further into why is thepkgnamenull.
It appears that pkgname is NOT set for a specific node. What is the node when "null.*" import is created?
#10 Updated by Dănuț Filimon almost 2 years ago
The jroot JavaAst when calling createImport() is:
compilation unit [COMPILE_UNIT]:68719476737 @0:0
com.goldencode.p2j.persist.dynquery [KW_PACKAGE]:68719476738 @0:0
com.goldencode.p2j.util.* [KW_IMPORT]:68719476739 @0:0
com.goldencode.p2j.util.BlockManager.* [STATIC_IMPORT]:68719476740 @0:0
DynGenQuery [KW_CLASS]:68719476741 @0:0
[CS_CONSTANTS]:68719476742 @0:0
[CS_STATIC_VARS]:68719476743 @0:0
[CS_STATIC_INITS]:68719476744 @0:0
[CS_INSTANCE_VARS]:68719476745 @0:0
[CS_CONSTRUCTORS]:68719476746 @0:0
[CS_STATIC_METHODS]:68719476747 @0:0
[CS_INSTANCE_METHODS]:68719476748 @0:0
execute [METHOD_DEF]:68719476750 @0:0
[BLOCK]:68719476751 @0:0
[BOGUS]:68719476752 @0:0
[SKIP]:68719476753 @0:0
externalProcedure [STATIC_METHOD_CALL]:68719476754 @0:0
DynGenQuery.this [REFERENCE]:68719476755 @0:0
Block [CONSTRUCTOR]:68719476756 @0:0
Pre [CAST]:68719476757 @0:0
[LAMBDA]:68719476758 @0:0
[LPARENS]:68719476759 @0:0
block [BLOCK]:68719476760 @0:0
Init [CAST]:68719476761 @0:0
[LAMBDA]:68719476762 @0:0
[LPARENS]:68719476763 @0:0
block [BLOCK]:68719476764 @0:0
Enter [CAST]:68719476765 @0:0
[LAMBDA]:68719476766 @0:0
[LPARENS]:68719476767 @0:0
block [BLOCK]:68719476768 @0:0
Body [CAST]:68719476769 @0:0
[LAMBDA]:68719476770 @0:0
[LPARENS]:68719476771 @0:0
block [BLOCK]:68719476772 @0:0
Fini [CAST]:68719476773 @0:0
[LAMBDA]:68719476774 @0:0
[LPARENS]:68719476775 @0:0
block [BLOCK]:68719476776 @0:0
[CS_INNER_CLASSES]:68719476749 @0:0
com.goldencode.p2j.persist.* [KW_IMPORT]:68719476777 @0:0
null.* [KW_IMPORT]:68719476778 @0:0
#11 Updated by Constantin Asofiei almost 2 years ago
If you add -Drules.tracing=true at the FWD server, I think the runtime conversion will emit the by-rule annotations, which can help you find where the null.* import is emitted. So you can do a root.dumpTree(true) to get the full annotations.
#12 Updated by Ovidiu Maxiniuc almost 2 years ago
Dănuț Filimon wrote:
The jroot JavaAst when calling createImport() is:
[...]
I was not referring to the JAST, but to this Progress AST, at the moment when the KW_IMPORT:68719476778 is created.
Indeed, -Drules.tracing=true will reveal the point where the node is created.
Unfortunately, will not help you with the core issue. It will trace things which have been done, in this case we are missing a pkgname annotation.
However, inserting a printfln("%s", copy.dumpTree(true)) will reveal the node type/text and might help understand whet is wrong with this node.
#13 Updated by Constantin Asofiei almost 2 years ago
Also, about the ErrorConditionException and NO-ERROR mode: I don't think we can aggressively change this so that the ErrorConditionException is not being thrown, and instead the logic knows to 'return safely' from the call, without side-effects. For example, it is assumed that each and every recordOrShowError or recordOrThrowError is followed by a Java return, but (depending on how deep in the call for this i.e. DELETE OBJECT operation we are), this does not mean that the DELETE will stop right there and there if no exception is thrown. That's why I think first is to identify which are the most important cases, and address them one-by-one. For example, even for DELETE OBJECT, it depends on the target resource.
Greg: the point of the above is to not raise an ErrorConditionException when in silent mode, but gracefully 'abort' the operation without the exception, and let the stack safely unwind without side-effects.
Also, there was another idea mentioned by Alexandru: for FIND ... NO-ERROR cases, instead of emitting the silent(() -> lambda, emit a i.e. silentFirst() which avoids the lambda and lets the code know this is a NO-ERROR code and (considering the previous above point) also not rely on ErrorConditionException to know that the call failed.
#14 Updated by Dănuț Filimon almost 2 years ago
Ovidiu Maxiniuc wrote:
However, inserting a
printfln("%s", copy.dumpTree(true))will reveal the node type/text and might help understand whet is wrong with this node.
I tried to do this in the rules, but I am not sure where this output of the printfln is displayed.
#15 Updated by Constantin Asofiei almost 2 years ago
Dănuț Filimon wrote:
Ovidiu Maxiniuc wrote:
However, inserting a
printfln("%s", copy.dumpTree(true))will reveal the node type/text and might help understand whet is wrong with this node.I tried to do this in the rules, but I am not sure where this output of the printfln is displayed.
printfln will be in the FWD server's log, but the logging level is FINE, so unless you adjust it, you will not see it.
#16 Updated by Dănuț Filimon almost 2 years ago
- Subject changed from Reduce the number of ClassNotFoundException and NoSuchMethodException that are being thrown to Reduce the number of exceptions that are being thrown
Constantin Asofiei wrote:
printflnwill be in the FWD server's log, but the logging level isFINE, so unless you adjust it, you will not see it.
Thanks! The node is:
DEFINE [DEFINE_TEMP_TABLE]:12884901931 @0:0
(ByRule=null:0, reachable=true, schemaname=BItttable_tt_BItttable_1, bufname=BItttable_tt_BItttable_1, dbname=, force_dmo_alias=BItttable, bufdefkey=BItttable_tt_BItttable_1,BItttable_tt_BItttable_1, bufdefkey-ByRule=null:0, javaname=BItttable, javaname-ByRule=null:0, classname=BItttable_1, classname-ByRule=null:0, bufclassname=BItttable_1.Buf, bufclassname-ByRule=null:0, pkgname=null, pkgname-ByRule=null:0, bufreftype=18, bufreftype-ByRule=null:0, uniquename=BItttable_tt_BItttable_1_BItttable_tt_BItttable_1, uniquename-ByRule=null:0)
TEMP-TABLE [KW_TEMP_TAB]:12884901932 @0:0
(ByRule=null:0, reachable=true)
BItttable_tt_BItttable_1 [SYMBOL]:12884901933 @0:0
(ByRule=null:0, reachable=true)
I added -Drules.tracing=true, but it seems the by-rule is not set properly.
#17 Updated by Greg Shah almost 2 years ago
Also, about the
ErrorConditionExceptionand NO-ERROR mode: I don't think we can aggressively change this so that theErrorConditionExceptionis not being thrown, and instead the logic knows to 'return safely' from the call, without side-effects. For example, it is assumed that each and everyrecordOrShowErrororrecordOrThrowErroris followed by a Java return, but (depending on how deep in the call for this i.e.DELETE OBJECToperation we are), this does not mean that the DELETE will stop right there and there if no exception is thrown. That's why I think first is to identify which are the most important cases, and address them one-by-one. For example, even forDELETE OBJECT, it depends on the target resource.Greg: the point of the above is to not raise an ErrorConditionException when in silent mode, but gracefully 'abort' the operation without the exception, and let the stack safely unwind without side-effects.
I agree this would be quite risky and it would be needed in a very large number of places.
One idea: have a configurable option to throw a single pre-constructed instance (per context) of ErrorConditionException instead of a newly constructed instance. We achieved good results in the past with this idea and it can be optional, so if we need the stack traces, we can always configure it for that.
#18 Updated by Dănuț Filimon almost 2 years ago
Greg Shah wrote:
One idea: have a configurable option to throw a single pre-constructed instance (per context) of
ErrorConditionExceptioninstead of a newly constructed instance. We achieved good results in the past with this idea and it can be optional, so if we need the stack traces, we can always configure it for that.
This is interesting, isn't this something similar that can be done to DeferredLegacyErrorException? Most of the time Each time, this instance is initialized with super(-1, null); which will also end up initializing a NumberedException with the same message and error code number (null, -1).
- NumberedException: Message null, Count 53133/1392278, Top 12
- Exception: Message null, Count 59400/59400
- DeferredLegacyErrorException: Message null, Count 53133/53133
- NumberFormatException: Message null, Count 34543/34543
#19 Updated by Greg Shah almost 2 years ago
Yes, we do this already. Any exception that doesn't need a stack trace is a candidate for this "trick". It saves a LOT of CPU.
#20 Updated by Dănuț Filimon almost 2 years ago
- Reduce the number of DeferredLegacyErrorException by relying on a single instantiated objected in ErrorManager.
- Reduce the number of NumberFormatException being thrown by checking if the node exists first in XmlImport.
- Revert null import path check, the null was actually a "null.*" (string), but it was not a checked path as it would find com.goldencode.p2j.persist import path as the match before it even reached it.
#21 Updated by Dănuț Filimon almost 2 years ago
I tested a customer scenario, 100 runs with 9032a/15268 and com.goldencode.p2j.util.DeferredLegacyErrorException does not appear in the Exceptions at all and java.lang.NumberFormatException has been reduced from 34k to a single exception.
Ovidiu Maxiniuc wrote:
Dănuț Filimon wrote:
This is attributed entirely to moving the "java.lang.*" import path at the end, although I still have ClassNotFoundException caused by searching classes such as
com.goldencode.p2j.util.FieldRefernce, com.goldencode.p2j.util.AdaptiveQuery, com.goldencode.p2j.util.RandomAccessQuery, com.goldencode.p2j.util.CompoundQuery, com.goldencode.p2j.util.QueryConstantswhich are actually from the persist package.OK. I understand. Here is an idea of how this can be avoided. We can keep a static map with hints for the package (maybe better the .class) of the most used class names, like
FieldRefernce,AdaptiveQuery, etc. Before iterating the imports we can use this map quickly identify the correct class/package. However, this is a highly heuristic approach and only the name of the classes which we are sure are unique should be hinted. This can actually improve the resolution performance.
I really wanted to come back to this note from #9032-5 and say that I like the idea, but I don't think this is the right thing to do as we will need to keep track of this in the future when we make changes to the respective classes or add new ones. I'd rather try finding where the KW_IMPORT package for com.goldencode.p2j.util.* is added and maybe put com.goldencode.p2j.persist.* before it.
#22 Updated by Dănuț Filimon almost 2 years ago
Discussed during yesterday's meeting, relying on other exceptions to fill the callStack of the error for a LegacyErrorException is not possible as the exceptions do not have any information that can be traced back to the callStack as it is populated only when the LegacyErrorException is created.
#23 Updated by Dănuț Filimon almost 2 years ago
Ovidiu, do you have any small example that will reach createImport()? I've tried to create one similar to one of the patterns used in the application (before buffer, dynamic query) but I did not manage to reproduce it.
#24 Updated by Dănuț Filimon almost 2 years ago
Something I thought of based on #9032-17, we can have a single ErrorConditionException instance that can be thrown for each "fixed" error message. By fixed, I mean a message that does not require formatting tokens (replacing %1, %2 and so on). There's a list of such cases in ErrorManager.replaceTokens(), for example:
case 143: msg = "** %1: Unable to evaluate field for assignment"; break; <- token needs to be replaced, exception message will not be the same
case 223: msg = "** Incompatible data types in expression or assignment"; break; <- no tokens, exception message will be the same for each created instance
#25 Updated by Ovidiu Maxiniuc almost 2 years ago
Dănuț Filimon wrote:
Something I thought of based on #9032-17, we can have a single ErrorConditionException instance that can be thrown for each "fixed" error message. By fixed, I mean a message that does not require formatting tokens (replacing %1, %2 and so on). There's a list of such cases in
ErrorManager.replaceTokens(), for example:
[...]
Just a background first:
The content of the values of the switch was extracted from 4GL GUI. In Help menu of the procedure editor, the last two entries are: Messages... and Recent Messages.... The former allows the user to lookup a specific error message, the latter lets you navigate to latest errors encountered by your application. The tokens can be seen in <>. The final idea for this was to have the switch morphed into something else (map/table) which would allow easier translation of messages.
Back to your idea: indeed, the errors are (if not, they probably should be) immutable objects. Based on this observation, if I understand correctly, you hope that some kind of prototype for those which have no parameters to be re-used each time such an error is needed. This would avoid short-lived creation of all instances for the specified ID and probably will have some performance benefits.
However, the problem for the parametrised errors still remains. And those form the majority. Even I tried to optimize as much as possible the token replacement algorithm, this might be still a problem. I am thinking of delaying this operation, for as much as possible. Working in lazy mode: keep the error ID and the table of replacements in the object, but only 'assembling' the final message only if it is required. Since the error check is normally performed using the ID and the message is printed only in exceptional cases, the replaceTokens() will not be executed.
Note that not errors are raised using this relatively new API. There are lots of places where the message is inlined (using + concatenation or local String builders) or even worse, using String.format(). The latter has a huge impact, since it will use advanced pattern matching for identifying placeholders and, as noted above, the resulted value (message String) will never be used.
#26 Updated by Ovidiu Maxiniuc almost 2 years ago
Dănuț Filimon wrote:
Ovidiu, do you have any small example that will reach
createImport()? I've tried to create one similar to one of the patterns used in the application (before buffer, dynamic query) but I did not manage to reproduce it.
Using a built-in 4GL function which FWD implemented in a different package should do the trick.
Creating a dynamic query for the FILL or SAVE-CHANGES methods in a Dataset with the DataSource from a different (permanent) database should add the imports needed for that related DMO.
#27 Updated by Dănuț Filimon almost 2 years ago
Ovidiu Maxiniuc wrote:
Back to your idea: indeed, the errors are (if not, they probably should be) immutable objects. Based on this observation, if I understand correctly, you hope that some kind of prototype for those which have no parameters to be re-used each time such an error is needed. This would avoid short-lived creation of all instances for the specified ID and probably will have some performance benefits.
However, the problem for the parametrised errors still remains. And those form the majority. Even I tried to optimize as much as possible the token replacement algorithm, this might be still a problem. I am thinking of delaying this operation, for as much as possible. Working in lazy mode: keep the error ID and the table of replacements in the object, but only 'assembling' the final message only if it is required. Since the error check is normally performed using the ID and the message is printed only in exceptional cases, the
replaceTokens()will not be executed.Note that not errors are raised using this relatively new API. There are lots of places where the message is inlined (using
+concatenation or localStringbuilders) or even worse, usingString.format(). The latter has a huge impact, since it will use advanced pattern matching for identifying placeholders and, as noted above, the resulted value (message String) will never be used.
You are right. Assembling the final message sounds like a good idea, but there's the problem of errors messages built with concatenation or String.format() which doesn't make use of this change. I think the right thing to do is to get rid of this type of operations from error building first so that we are able to get closer to using the new API and then focus on the next steps that can be taken.
#28 Updated by Dănuț Filimon almost 2 years ago
| Test | Run | Total for run | Total processing for run | avg of last 20 runs | avg processing of last 20 | total avg | total processing avg |
|---|---|---|---|---|---|---|---|
| 7156b/15110 | 1 | 18004 | 18004 | 18004 | 18004 | 18004 | 18004 |
| 7156b/15110 | 2 | 14933 | 14933 | 16468 | 16468 | 16468 | 16468 |
| 7156b/15110 | 3 | 14677 | 14677 | 15871 | 15871 | 15871 | 15871 |
| 7156b/15110 | 4 | 14141 | 14141 | 15438 | 15438 | 15438 | 15438 |
| 7156b/15110 | 5 | 14025 | 14025 | 15156 | 15156 | 15156 | 15156 |
| 7156b/15110 | 96 | 13175 | 13175 | 13081 | 13081 | 13279 | 13279 |
| 7156b/15110 | 97 | 13130 | 13130 | 13088 | 13088 | 13278 | 13278 |
| 7156b/15110 | 98 | 13169 | 13169 | 13095 | 13095 | 13277 | 13277 |
| 7156b/15110 | 99 | 13066 | 13066 | 13097 | 13097 | 13275 | 13275 |
| 7156b/15110 | 100 | 13280 | 13280 | 13097 | 13097 | 13275 | 13275 |
| 7156b/15110 + 9032a | 1 | 17484 | 17484 | 17484 | 17484 | 17484 | 17484 |
| 7156b/15110 + 9032a | 2 | 14805 | 14805 | 16144 | 16144 | 16144 | 16144 |
| 7156b/15110 + 9032a | 3 | 14521 | 14521 | 15603 | 15603 | 15603 | 15603 |
| 7156b/15110 + 9032a | 4 | 13946 | 13946 | 15189 | 15189 | 15189 | 15189 |
| 7156b/15110 + 9032a | 5 | 13981 | 13981 | 14947 | 14947 | 14947 | 14947 |
| 7156b/15110 + 9032a | 96 | 13350 | 13350 | 13342 | 13342 | 13362 | 13362 |
| 7156b/15110 + 9032a | 97 | 13411 | 13411 | 13324 | 13324 | 13363 | 13363 |
| 7156b/15110 + 9032a | 98 | 13319 | 13319 | 13330 | 13330 | 13362 | 13362 |
| 7156b/15110 + 9032a | 99 | 13343 | 13343 | 13338 | 13338 | 13362 | 13362 |
| 7156b/15110 + 9032a | 100 | 13433 | 13433 | 13337 | 13337 | 13363 | 13363 |
The table contains the first and last 5 runs, with and without the 9032a changes. There's a 500 ms improvement for the first run, then is starts to decrease to around 100ms for the following runs. But the last 5 runs are showing a worse time with 9032a (200-300ms).
#29 Updated by Constantin Asofiei almost 2 years ago
- Related to Bug #8272: reduce ClassNotFoundException and NoSuchMethodException usage in RuntimeJastInterpreter added
#30 Updated by Constantin Asofiei almost 2 years ago
- Related to Bug #8273: avoid ErrorConditionException for FIND ... NO-ERROR if no record is found added
#31 Updated by Dănuț Filimon almost 2 years ago
I've made a few adjustments to reduce the search for null parameter types. Instead of using Arrays.stream(parTypes).anyMatch(v -> v == null);, it is more favorable to check for them while collecting the parameters so that we are aware that there are null values from the start.
#32 Updated by Dănuț Filimon almost 2 years ago
Committed 9032a/15369. Removed usage of Arrays.stream(), incorporated the functionality in a separate method named collectParameters() - it collects the signature and values, the return value is used to determine if the signature has null parameter types.
#33 Updated by Dănuț Filimon almost 2 years ago
Ovidiu Maxiniuc wrote:
Dănuț Filimon wrote:
Ovidiu, do you have any small example that will reach
createImport()? I've tried to create one similar to one of the patterns used in the application (before buffer, dynamic query) but I did not manage to reproduce it.Using a built-in 4GL function which FWD implemented in a different package should do the trick.
Creating a dynamic query for the FILL or SAVE-CHANGES methods in a Dataset with theDataSourcefrom a different (permanent) database should add the imports needed for that related DMO.
I tried to do an example with FILL and I finally managed to get to debug the collectImports()! Now I noted that when we create a dynamic query in this type of setting we have dmos for the tables (fwd, _meta, _temp) and all of them used the correct package, so the problem should be related to dynamically created tables or before buffers that do not have a dmo. I am getting close to creating a proper example for this.
#34 Updated by Dănuț Filimon almost 2 years ago
I got blocked again as I am not able to reproduce the "null.*" import, the latest example I managed to create is:
DEFINE TEMP-TABLE tt2 NO-UNDO BEFORE-TABLE btt2
FIELD f1 AS CHARACTER
FIELD f2 AS CHARACTER
FIELD f3 AS CHARACTER
FIELD f4 AS CHARACTER
FIELD f5 AS CHARACTER INDEX idx1 IS PRIMARY UNIQUE f1 f2 f3.
DEFINE VARIABLE h-tt AS HANDLE NO-UNDO.
DEFINE VARIABLE htt AS HANDLE NO-UNDO.
CREATE TEMP-TABLE h-tt.
h-tt:CREATE-LIKE('tt2').
h-tt:TEMP-TABLE-PREPARE ('tt1').
htt = h-tt:DEFAULT-BUFFER-HANDLE.
DEFINE VARIABLE ds1 AS HANDLE NO-UNDO.
DEFINE VARIABLE srctt2 AS HANDLE NO-UNDO.
DEFINE VARIABLE htt2 AS HANDLE NO-UNDO.
CREATE BUFFER htt2 FOR TABLE "tt2".
DEFINE VARIABLE qh AS HANDLE NO-UNDO.
CREATE QUERY qh.
qh:ADD-BUFFER(htt2).
CREATE DATASET ds1.
ds1:ADD-BUFFER(htt).
h-tt:TRACKING-CHANGES = TRUE.
htt:BUFFER-CREATE().
htt:BUFFER-FIELD("f1"):BUFFER-VALUE(0) = "A6".
htt:BUFFER-FIELD("f2"):BUFFER-VALUE(0) = "B6".
htt:BUFFER-FIELD("f3"):BUFFER-VALUE(0) = "C6".
htt:BUFFER-FIELD("f4"):BUFFER-VALUE(0) = "D6".
htt:BUFFER-FIELD("f5"):BUFFER-VALUE(0) = "E6".
h-tt:TRACKING-CHANGES = FALSE.
CREATE DATA-SOURCE srctt2.
srctt2:QUERY = qh.
htt:ATTACH-DATA-SOURCE(srctt2).
DO TRANSACTION ON ERROR UNDO, LEAVE:
qh:SET-BUFFERS(ds1:GET-BUFFER-HANDLE(1):BEFORE-BUFFER).
qh:QUERY-PREPARE("FOR EACH " + ds1:GET-BUFFER-HANDLE(1):BEFORE-BUFFER:TABLE).
qh:QUERY-OPEN().
qh:GET-FIRST().
DO WHILE NOT qh:QUERY-OFF-END ON ERROR UNDO, LEAVE:
ds1:GET-BUFFER-HANDLE(1):BEFORE-BUFFER:SAVE-ROW-CHANGES().
qh:GET-NEXT().
END.
qh:QUERY-CLOSE().
END.
htt:DETACH-DATA-SOURCE().
DELETE OBJECT srctt2.
Compared to the tree dump when collectImportPaths() is called (from #9032-10), the dump looks like this:
compilation unit [COMPILE_UNIT]:25769803777 @0:0
com.goldencode.p2j.util.* [KW_IMPORT]:25769803779 @0:0
com.goldencode.p2j.util.BlockManager.* [STATIC_IMPORT]:25769803780 @0:0
DynGenQuery [KW_CLASS]:25769803781 @0:0
(access=public, pkgname=com.goldencode.p2j.persist.dynquery)
[CS_INSTANCE_VARS]:25769803785 @0:0
= [ASSIGN]:25769803850 @0:0
AdaptiveQuery [REFERENCE_DEF]:25769803851 @0:0
(name=query0, final=false)
AdaptiveQuery [CONSTRUCTOR]:25769803852 @0:0
(runtime-class=class com.goldencode.p2j.persist.AdaptiveQuery, runtime-constructor=public com.goldencode.p2j.persist.AdaptiveQuery())
[CS_INSTANCE_METHODS]:25769803788 @0:0
execute [METHOD_DEF]:25769803790 @0:0
[BLOCK]:25769803791 @0:0
initialize [METHOD_CALL]:25769803855 @0:0
(runtime-class=class com.goldencode.p2j.persist.AdaptiveQuery, runtime-method=public com.goldencode.p2j.persist.PreselectQuery com.goldencode.p2j.persist.PreselectQuery.initialize(com.goldencode.p2j.persist.DataModelObject,java.lang.String,java.util.function.Supplier,java.lang.String,java.lang.String))
query0 [REFERENCE]:25769803856 @0:0
bitt1 [REFERENCE]:25769803857 @0:0
String [CAST]:25769803858 @0:0
(runtime-class=class java.lang.String)
[NULL_LITERAL]:25769803859 @0:0
(runtime-value=null)
[NULL_LITERAL]:25769803860 @0:0
(runtime-value=null)
bitt1._rowState asc [STRING]:25769803861 @0:0
(runtime-value=bitt1._rowState asc)
WHOLE-INDEX,rowState [STRING]:25769803862 @0:0
(runtime-value=WHOLE-INDEX,rowState)
com.goldencode.p2j.persist.* [KW_IMPORT]:25769803817 @0:0
com.goldencode.p2j.persist.dynamic._temp.* [KW_IMPORT]:25769803818 @0:0
com.goldencode.p2j.persist.lock.* [KW_IMPORT]:25769803863 @0:0
#35 Updated by Dănuț Filimon almost 2 years ago
- % Done changed from 30 to 50
- Status changed from WIP to Review
| Test | Run | Total for run | Total processing for run | avg of last 20 runs | avg processing of last 20 | total avg | total processing avg |
|---|---|---|---|---|---|---|---|
| 7156b/15110 | 1 | 18700 | 18700 | 18700 | 18700 | 18700 | 18700 |
| 7156b/15110 | 2 | 15372 | 15372 | 17036 | 17036 | 17036 | 17036 |
| 7156b/15110 | 3 | 14805 | 14805 | 16292 | 16292 | 16292 | 16292 |
| 7156b/15110 | 4 | 14285 | 14285 | 15790 | 15790 | 15790 | 15790 |
| 7156b/15110 | 5 | 14113 | 14113 | 15455 | 15455 | 15455 | 15455 |
| 7156b/15110 | 96 | 13239 | 13239 | 13221 | 13221 | 13427 | 13427 |
| 7156b/15110 | 97 | 13287 | 13287 | 13228 | 13228 | 13425 | 13425 |
| 7156b/15110 | 98 | 13288 | 13288 | 13233 | 13233 | 13424 | 13424 |
| 7156b/15110 | 99 | 13275 | 13275 | 13225 | 13225 | 13422 | 13422 |
| 7156b/15110 | 100 | 13288 | 13288 | 13224 | 13224 | 13421 | 13421 |
| 7156b/15110 + 9032a | 1 | 18021 | 18021 | 18021 | 18021 | 18021 | 18021 |
| 7156b/15110 + 9032a | 2 | 15128 | 15128 | 16574 | 16574 | 16574 | 16574 |
| 7156b/15110 + 9032a | 3 | 14926 | 14926 | 16025 | 16025 | 16025 | 16025 |
| 7156b/15110 + 9032a | 4 | 14346 | 14346 | 15605 | 15605 | 15605 | 15605 |
| 7156b/15110 + 9032a | 5 | 14215 | 15215 | 15327 | 15327 | 15327 | 15327 |
| 7156b/15110 + 9032a | 96 | 13425 | 13425 | 13415 | 13415 | 13534 | 13534 |
| 7156b/15110 + 9032a | 97 | 13491 | 13491 | 13426 | 13426 | 13533 | 13533 |
| 7156b/15110 + 9032a | 98 | 13488 | 13488 | 13432 | 13432 | 13533 | 13533 |
| 7156b/15110 + 9032a | 99 | 13471 | 13471 | 13426 | 13426 | 13532 | 13532 |
| 7156b/15110 + 9032a | 100 | 13548 | 13548 | 13442 | 13442 | 13532 | 13532 |
Similar to #9032-28, there is an improvement for the first runs but it is showing a worse time at the end. I wonder if there is anything I can improve, please review.
#36 Updated by Dănuț Filimon almost 2 years ago
Created 9032b, this branch will contain conversion changes related to FindQuery silent operations with the purpose of avoiding lambdas. I've made a basic implementation and right now I am working on reducing the rules used to a much simpler form, should be available tomorrow.
#37 Updated by Greg Shah almost 2 years ago
Ovidiu: Please review.
#38 Updated by Ovidiu Maxiniuc almost 2 years ago
Review of 9032a/15367-9.
I think the code is good. There are some issue I spotted:
RuntimeJastInterpreter.java:- line 917: the line can be dropped: the
assertis not be meant for parameter validation; - line 928:
hasNullParameterTypeis correctly evaluated only ifsignature != null. Otherwisefalseis always returned, even there are some nulls invalues; - in places where the 'enhanced for' was replaced by indexed variant and there is a single occurrence of the iterated element (3 locations), it can be inlined;
- line 917: the line can be dropped: the
XmlImport.java:- line 2376: since you initialized
decimalswhen it was declared, this line can be dropped, or better, replaced with a comment saying that in case of NFE, the value ofdecimalsremains 0. I wonder whether Java ♨️ is smart enough to remove it at compile time;
- line 2376: since you initialized
ControlFlowOps.java:- line 9837: shouldn't the condition be
if (p == null || p instanceof unknown)?
- line 9837: shouldn't the condition be
#39 Updated by Dănuț Filimon almost 2 years ago
Ovidiu Maxiniuc wrote:
Review of 9032a/15367-9.
I think the code is good. There are some issue I spotted:
ControlFlowOps.java:
- line 9837: shouldn't the condition be
if (p == null || p instanceof unknown)?
Not at all, this change only targets converted calls such as:
RUN_CALL_SITE_6.clone().run(new unknown());
Even if there are cases where values are passed, those are BaseDataType, handles which by default are initialized with unknown so in the first place I don't think it is possible to have a null parameter (I checked hundreds RUN_CALL_SITE calls in a customer project and found no such occurrence, but please do correct me if I am wrong). There are also other types that can be passed such as DatasetParameter and so on.
Fixed in 9032a/15370.
#40 Updated by Dănuț Filimon almost 2 years ago
- Status changed from Review to WIP
Committed 9032b/15379. Initial implementation for emitting a silent method instead of a lambda for FIND [FIRST|NEXT|PREV|LAST] ... NO-ERROR. The targeted methods are: first(), next(), last(), previous(), unique(), current().
#41 Updated by Dănuț Filimon almost 2 years ago
Tested hotel_gui conversion and found the the following problem:
[javac] Compiling 117 source files to /home/ddf/gcd/hotel_gui/build/classes
[javac] Compiling 61 source files to /home/ddf/gcd/hotel_gui/build/classes
[javac] /home/ddf/gcd/hotel_gui/src/com/goldencode/hotel/h2_tester/QueriesTester.java:458: error: ';' expected
[javac] }).silentFirst()for (i.assign(1); _isLessThanOrEqual(i, 1000); i.increment())
[javac] ^
[javac] /home/ddf/gcd/hotel_gui/src/com/goldencode/hotel/h2_tester/QueriesTester.java:488: error: ';' expected
[javac] }).silentFirst()for (i.assign(1); _isLessThanOrEqual(i, 1000); i.increment())
[javac] ^
[javac] /home/ddf/gcd/hotel_gui/src/com/goldencode/hotel/h2_tester/QueriesTester.java:518: error: ';' expected
[javac] }).silentFirst()for (i.assign(1); _isLessThanOrEqual(i, 1000); i.increment())
[javac] ^
[javac] 3 errors
because the following:
//------------------------------------------------------
iRowsAffected = 0.
ETIME(YES).
DO i = 1 TO 1000 :
FIND FIRST tt2 WHERE tt2.f1 = (i * 9) MOD 900 NO-ERROR.
END.
tm = ETIME(NO).
RUN createField IN hTools1 (INPUT "find", INPUT "FIND-FIRST-WHERE-FIELD-=", INPUT iRowsAffected, INPUT tm, INPUT-OUTPUT TABLE t-field).
//------------------------------------------------------
iRowsAffected = 0.
ETIME(YES).
DO i = 1 TO 1000 :
FIND FIRST tt2 WHERE tt2.fId > i * 9.
END.
tm = ETIME(NO).
RUN createField IN hTools1 (INPUT "find", INPUT "FIND-FIRST-WHERE-INDEX->", INPUT iRowsAffected, INPUT tm, INPUT-OUTPUT TABLE t-field).
//------------------------------------------------------
is converted like:
// ------------------------------------------------------
iRowsAffected.assign(0);
date.elapsed(true);
loopLabel15:
new FindQuery(tt2, "tt2.f1 = ?", null, "tt2.fid asc", new Object[]
{
modulo(multiply(i, 9), 900)
}).silentFirst()for (i.assign(1); _isLessThanOrEqual(i, 1000); i.increment())
;
tm.assign(date.elapsed(false));
RUN_CALL_SITE_15.clone().setInHandle(hTools1).run(new character("find"), new character("FIND-FIRST-WHERE-FIELD-="), iRowsAffected, tm, new TableParameter(tField));
// ------------------------------------------------------
iRowsAffected.assign(0);
date.elapsed(true);
loopLabel16:
for (i.assign(1); _isLessThanOrEqual(i, 1000); i.increment())
{
new FindQuery(tt2, "tt2.fid > ?", null, "tt2.fid asc", new Object[]
{
multiply(i, 9)
}).first();
}
tm.assign(date.elapsed(false));
RUN_CALL_SITE_16.clone().setInHandle(hTools1).run(new character("find"), new character("FIND-FIRST-WHERE-INDEX->"), iRowsAffected, tm, new TableParameter(tField));
// ------------------------------------------------------
Currently investigating.
#42 Updated by Dănuț Filimon almost 2 years ago
A peer ast is created correctly for:
[java] block [BLOCK]:317827580127 @0:0 [java] (peerid-byrule=convert/language_statements.rules:281, byrule=convert/language_statements.rules:281, peerid=21474836784)but with 9032b it is created for:
[java] block [BLOCK]:317827580123 @0:0 [java] (peerid-byrule=convert/control_flow.rules:2532, byrule=convert/control_flow.rules:2532, peerid=21474836783)
#43 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
A peer ast is created correctly for:
[...] but with 9032b it is created for:
[...]
If I don't use NO-ERROR, the FindQuery is generated correctly on a similar block when using 9032b.
#44 Updated by Dănuț Filimon almost 2 years ago
I tested the same customer scenario from #9032-35 (100 runs) using 9032a and monitored the process using VisualVM. There's no memory leak and the heap size was slowly decreasing (2h 45 min server uptime, the scenario took ~40 minutes).
#45 Updated by Dănuț Filimon almost 2 years ago
- no brackets used for the body of a block
// 4GL code METHOD PUBLIC VOID create1(): FIND tt1 NO-ERROR. END METHOD. // 9032b @LegacySignature(type = Type.METHOD, name = "create1") public void create1() { internalProcedure(FindQueryUnique1noerror.class, this, "create1", new Block((Body) () -> new FindQuery(tt1, (String) null, null, "tt1.recid asc").silentUnique())); } // 4GL code, same method but without NO-ERROR METHOD PUBLIC VOID create1(): FIND tt1. END METHOD. // 9032b @LegacySignature(type = Type.METHOD, name = "create1") public void create1() { internalProcedure(FindQueryUnique1.class, this, "create1", new Block((Body) () -> { new FindQuery(tt1, (String) null, null, "tt1.recid asc").unique(); })); }If there is any other operation that is in the same procedure/block with the FindQuery, it will convert correctly. - no brackets used for IF ... THEN DO
// 4GL code METHOD PUBLIC VOID create49(): IF i EQ TRUE THEN DO: FIND tt1 NO-ERROR. END. END METHOD. // 9032b @LegacySignature(type = Type.METHOD, name = "create49") public void create49() { internalProcedure(FindQueryUnique1noerror.class, this, "create49", new Block((Body) () -> { if (_isEqual(i, true)) new FindQuery(tt1, (String) null, null, "tt1.recid asc").silentUnique() })); }If there is any other operation that is in the if statement with the FindQuery, it will convert correctly. - #9032-41
There are more cases where the FindQuery does not convert correctly, but all of them have the missing brackets in common. My implementation idea was to replace the methodtxt = "first" with "silentFirst" when the statement node would have the "no-silent-lambda" annotation and my intention was to let it generate like a normal first() call. I am currently revising the implementation because I can create a very small test case and comparing the queries with and without NO-ERROR to maybe identify where it is branching off.
#46 Updated by Dănuț Filimon almost 2 years ago
The problem with point 1 from #9032-45 is that when the rules reach include/common-progress.rules:11795 it will call:
<!-- now we can create the node to be returned -->
<rule>
retNode = tw.graft("block_lambda_def", blockNode, destNodeId,
"parent", constructor,
"finiType", finiType)
</rule>
where the block_lambda_def is a template from rules/convert/java_templates.tpl which should generate:
<ast col="0" line="0" text="" type="LPARENS">
<annotation datatype="java.lang.String" key="byrule" value="include/common-progress.rules:11795"/>
</ast>
<ast col="0" line="0" text="block" type="BLOCK">
<annotation datatype="java.lang.String" key="byrule" value="include/common-progress.rules:11795"/>
<annotation datatype="java.lang.String" key="peerid-byrule" value="include/common-progress.rules:11795"/>
...
but the BLOCK is missing for the NO-ERROR which is quite strange because both scenarios reach the same rule and make the same call but there is a block missing. Digging deeper...#47 Updated by Dănuț Filimon almost 2 years ago
I thought that the METHOD_CALL AST overrides the BLOCK AST in database_access.rule:1267 but I checked the created ast/parent and have the following AST after the call:
block [BLOCK]:274877907050 @0:0
[java] (byrule=include/common-progress.rules:11795, peerid-byrule=include/common-progress.rules:11795, peerid=90194313237)
[java] silentUnique [METHOD_CALL]:274877907056 @0:0
[java] (byrule=convert/database_access.rules:1267, peerid-byrule=convert/database_access.rules:1267, peerid=90194313239)
so at this point I am not sure how the BLOCK ast is even removed and where should I look at for this. Right now I am thinking of going over each rule from the .jast file and checking the whole tree if possible to see if I can point out in between what rules this Ast is wiped.#48 Updated by Dănuț Filimon almost 2 years ago
- % Done changed from 50 to 60
Dănuț Filimon wrote:
I thought that the METHOD_CALL AST overrides the BLOCK AST in database_access.rule:1267 but I checked the created ast/parent and have the following AST after the call:
[...] so at this point I am not sure how the BLOCK ast is even removed and where should I look at for this. Right now I am thinking of going over each rule from the .jast file and checking the whole tree if possible to see if I can point out in between what rules this Ast is wiped.
Caught the culprit! I looked into methods that might be used to remove nodes from AnnotatedAst such as remove(), removeChildren(), move() that include the prog.kw_no_error in the rules and found rules/convert/beautification.rules which only eliminates the block node for a silent mode lambda when there is a single line inside it. Fixed it quickly with a small patch and it solves the problem. I will commit this fix immediately after checking if I can make it simpler.
Now I have another conversion issue where an AdaptiveQuery ends up using the silent methods so I will also begin my investigation on this one.
#49 Updated by Dănuț Filimon almost 2 years ago
Committed 9032a/15380 9032b/15380. Fixed deleted BLOCK node for FIND ... NO-ERROR when there is no silent lambda.
#50 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
Now I have another conversion issue where an AdaptiveQuery ends up using the silent methods so I will also begin my investigation on this one.
This issue is actually related to AdaptiveFind, the methods are replaced correctly but are not implemented.
Alexandru, what do you think about removing the silent lambda for the AdaptiveFind query? I'll only have to implement the methods for this. The second option is opt AdaptiveFind from using silent methods and continue to use the silent lambda (still need to look into how to do this, but it will take more time).
#51 Updated by Alexandru Lungu almost 2 years ago
It is right to extend the optimization to the AdaptiveFind as well.
#52 Updated by Dănuț Filimon almost 2 years ago
Committed 9032b/15381. Added silentFirst() and silentNext() methods for AdaptiveFind.
My conversion tests pass now and the project compiles with no problems. I am switching to creating using tests and comparing the execution results with/without the changes. This will take a while as I need to make sure I cover most of the scenarios, I also think we have tests for the FindQuery in the testcases project so I'll also take a look there.
#53 Updated by Dănuț Filimon almost 2 years ago
- Status changed from WIP to Review
I'm done with the initial set of conversion/runtime tests and I got the same results with/without the changes from 9032b. Please review.
#54 Updated by Greg Shah almost 2 years ago
Alexandru: Please review.
#55 Updated by Constantin Asofiei almost 2 years ago
- Related to Bug #9078: emit FindQuery as (reused) variables added
#56 Updated by Alexandru Lungu almost 2 years ago
The changes are quite extensive and I will need a bit more time to get through them. I will re-schedule the review attempt tomorrow morning.
#57 Updated by Dănuț Filimon almost 2 years ago
I found issues while running performance tests. It seems that FIND NEXT, FIND PREV are not actually executing. Currently investigating...
#58 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
I found issues while running performance tests. It seems that FIND NEXT, FIND PREV are not actually executing. Currently investigating...
There is a scenario where a QueryOffEndException is thrown when calling silent methods which is not caught at the method level and causes the program to stop executing.
#59 Updated by Alexandru Lungu almost 2 years ago
- Status changed from Review to WIP
I just updated the branch but didn't had the chance to properly review it. Please consider fixing the found problem and I will resume the review after.
#60 Updated by Dănuț Filimon almost 2 years ago
Alexandru Lungu wrote:
I just updated the branch but didn't had the chance to properly review it. Please consider fixing the found problem and I will resume the review after.
I am already testing the fix (just catching the QueryOffEndException in the silent method) but I am not sure if there is this error should be processed or left as it is.
#61 Updated by Dănuț Filimon almost 2 years ago
FIND ... WHERE field = i where i is the value assigned in DO i = 1 TO ...: (the number represents the number of find queries executed):
| Test | Without 9032b (avg ms) | With 9032b (avg ms) | Comparison |
|---|---|---|---|
| tt 10 FIND FIRST | 0.8 | 0.4 | < |
| tt 100 FIND FIRST | 3 | 2.4 | < |
| tt 1000 FIND FIRST | 45 | 39.2 | < |
| tt 10000 FIND FIRST | 2654.4 | 2626.4 | < |
| tt 10 FIND LAST | 8.4 | 7.2 | < |
| tt 100 FIND LAST | 52.8 | 52 | < |
| tt 1000 FIND LAST | 514.8 | 515.8 | > |
| tt 10000 FIND LAST | 5207.2 | 5197.6 | < |
| tt 10 FIND | 8 | 7.4 | < |
| tt 100 FIND | 53.4 | 51.4 | < |
| tt 1000 FIND | 534.8 | 510.4 | < |
| tt 10000 FIND | 5315.4 | 5107.8 | < |
| pt 10 FIND FIRST | 12.6 | 15 | > |
| pt 100 FIND FIRST | 109.8 | 135 | > |
| pt 1000 FIND FIRST | 1102.2 | 1316 | > |
| pt 10000 FIND FIRST | 11654.8 | 13151.8 | > |
| pt 10 FIND LAST | 13.4 | 13.8 | > |
| pt 100 FIND LAST | 124.8 | 132.4 | > |
| pt 1000 FIND LAST | 1234.4 | 1316 | > |
| pt 10000 FIND LAST | 11441.8 | 13065.2 | > |
| pt 10 FIND | 14.2 | 14 | > |
| pt 100 FIND | 127.8 | 132.8 | > |
| pt 1000 FIND | 1273 | 1312.4 | > |
| pt 10000 FIND | 12733.2 | 13033.6 | > |
- Without 9032b (measurements are in ms):
Test Run 1 (Cold) Run 2 Run 3 Run 4 Run 5 Average tt 10 FIND FIRST 2 1 0 1 0 0.8 tt 100 FIND FIRST 10 2 1 1 1 3 tt 1000 FIND FIRST 108 32 28 28 29 45 tt 10000 FIND FIRST 2450 2735 2693 2711 2683 2654.4 tt 10 FIND LAST 8 7 10 10 7 8.4 tt 100 FIND LAST 57 51 51 52 53 52.8 tt 1000 FIND LAST 500 513 528 503 530 514.8 tt 10000 FIND LAST 4864 5245 5315 5140 5472 5207.2 tt 10 FIND 9 6 7 7 11 8 tt 100 FIND 55 54 53 51 54 53.4 tt 1000 FIND 549 515 535 511 564 534.8 tt 10000 FIND 5337 5347 5220 5100 5573 5315.4 pt 10 FIND FIRST 5 15 14 14 15 12.6 pt 100 FIND FIRST 21 129 132 132 135 109.8 pt 1000 FIND FIRST 187 1319 1346 1321 1338 1102.2 pt 10000 FIND FIRST 5771 12792 13129 13279 13303 11654.8 pt 10 FIND LAST 11 14 13 14 15 13.4 pt 100 FIND LAST 105 128 126 131 134 124.8 pt 1000 FIND LAST 968 1277 1254 1319 1354 1234.4 pt 10000 FIND LAST 5384 12814 12545 13234 13232 11441.8 pt 10 FIND 15 13 14 15 14 14.2 pt 100 FIND 130 124 126 130 129 127.8 pt 1000 FIND 1257 1248 1265 1308 1287 1273 pt 10000 FIND 12551 12601 12606 13205 12703 12733.2
The cold run has the best results out of all the runs for 10k find queries especially on the FIND FIRST/LAST of a persistent table with the cold run showing ~5000ms while the rest of the runs exceed 10000ms.
- With 9032b (measurements are in ms):
Test Run 1 (Cold) Run 2 Run 3 Run 4 Run 5 Average tt 10 FIND FIRST 1 1 0 0 0 0.4 tt 100 FIND FIRST 9 1 0 1 1 2.4 tt 1000 FIND FIRST 84 27 29 27 29 39.2 tt 10000 FIND FIRST 2573 2625 2625 2633 2676 2626.4 tt 10 FIND LAST 9 7 6 7 7 7.2 tt 100 FIND LAST 55 50 53 51 51 52 tt 1000 FIND LAST 522 517 513 498 529 515.8 tt 10000 FIND LAST 5181 5196 5131 5128 5352 5197.6 tt 10 FIND 8 7 6 7 9 7.4 tt 100 FIND 54 52 49 50 52 51.4 tt 1000 FIND 520 506 505 508 512 510.4 tt 10000 FIND 5135 5036 5034 5152 5182 5107.8 pt 10 FIND FIRST 17 14 15 15 14 15.0 pt 100 FIND FIRST 140 134 132 135 134 135 pt 1000 FIND FIRST 1309 1300 1306 1333 1332 1316 pt 10000 FIND FIRST 12990 13190 12992 13437 13150 13151.8 pt 10 FIND LAST 14 14 14 14 13 13.8 pt 100 FIND LAST 132 132 138 133 127 132.4 pt 1000 FIND LAST 1259 1356 1353 1340 1272 1316 pt 10000 FIND LAST 12664 12991 13503 13479 12689 13065.2 pt 10 FIND 14 13 14 15 14 14 pt 100 FIND 126 126 150 132 130 132.8 pt 1000 FIND 1269 1272 1354 1365 1302 1312.4 pt 10000 FIND 12620 12647 13418 13543 12940 13033.6
The same can't be said when using 9032b.
#62 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
I am already testing the fix (just catching the QueryOffEndException in the silent method) but I am not sure if there is this error should be processed or left as it is.
Just catching the QueryOffEndException is not the fix! The root cause is that the FindQuery constructor relies on the following statement:
setLenientOffEnd(getBuffer().txHelper.errHlp.isSilent());to set the
lenientOffEnd flag and to not throw QueryOffEndException. The fix involves setting the lenientOffEnd flag at the start of each silent method.
I also noted during debugging that there are two lenientOffEnd flags (one in RandomAccessQuery and one in AbstractQuery) and after discussing with Eduard/Alexandru, this should not be the case and the one from RandomAccessQuery might have been left because of a wrong rebase/merge.
#63 Updated by Dănuț Filimon almost 2 years ago
- Status changed from WIP to Review
- % Done changed from 60 to 80
Committed 9032b/15382. Fixed QueryOffEndException being thrown in silent mode.
This fixes QOEE that was thrown because the lenientOffEnd flag was set based on the ErrorHelper.isSilent() value in the constructor (this means that it was always false, now that the constructor is called first this one needs to be set for each silent mode method).
I am more concerned about AdaptiveFind as it keeps a list of query initialization actions and setting the lenientOffEnd flag is done outside of this, maybe someone with more experience regarding AdaptiveFind can help with confirming if this is the right approach.
Planning to start another round of testing and redo the performance tests from #9032-61. Please review.
EDIT: The tests from #9032-61 were done using Java 8, going further I plan to use Java 17.
#64 Updated by Dănuț Filimon almost 2 years ago
| Test | Without 9032b (avg ms) | With 9032b (avg ms) | Comparison |
|---|---|---|---|
| tt 10 FIND FIRST | 0.4 | 0.4 | = |
| tt 100 FIND FIRST | 3.4 | 3.2 | < |
| tt 1000 FIND FIRST | 38.8 | 43 | > |
| tt 10000 FIND FIRST | 2419 | 2554.2 | > |
| tt 10 FIND LAST | 7.8 | 7.6 | < |
| tt 100 FIND LAST | 51.2 | 56 | > |
| tt 1000 FIND LAST | 497.4 | 539.4 | > |
| tt 10000 FIND LAST | 4923 | 5292.2 | > |
| tt 10 FIND | 9.4 | 7.8 | < |
| tt 100 FIND | 51.4 | 53 | > |
| tt 1000 FIND | 475.4 | 520.4 | > |
| tt 10000 FIND | 4611.6 | 5200.4 | > |
| pt 10 FIND FIRST | 13.2 | 11.4 | < |
| pt 100 FIND FIRST | 108.4 | 109 | > |
| pt 1000 FIND FIRST | 1083 | 1099.8 | > |
| pt 10000 FIND FIRST | 11406.2 | 11774 | > |
| pt 10 FIND LAST | 12.8 | 13.2 | > |
| pt 100 FIND LAST | 123.8 | 126.4 | > |
| pt 1000 FIND LAST | 1216.8 | 1256.8 | > |
| pt 10000 FIND LAST | 11309.4 | 11606.8 | > |
| pt 10 FIND | 14.4 | 13.6 | < |
| pt 100 FIND | 128.4 | 129.8 | > |
| pt 1000 FIND | 1269.6 | 1286.4 | > |
| pt 10000 FIND | 12727.8 | 12827.6 | > |
#65 Updated by Alexandru Lungu almost 2 years ago
Review on 9032b:
- In
AdaptiveFind, isn't better to do delegate thelenientOffEndstuff tosilent*method entirely. I don't like the fact that we are working with lenient off end both in silent* and non-silent* methods. Can we simply stick to how this is done inRandomAccessQuery? (a.k.a set the lenient-off end directly in the silent* methods?) - Please cache the
ErrorHelperas a class member. That is, set-it up in the initialize method, to avoid retrieving at each operation. - Rework
ErrorManager.silentWorkerto useprocessLegacyErrorException(which I think can be made static in ErrorManager anyway).
#66 Updated by Alexandru Lungu almost 2 years ago
Also, can you share here the test you are doing for profiling?
#67 Updated by Dănuț Filimon almost 2 years ago
- File perf.zip added
Alexandru Lungu wrote:
Also, can you share here the test you are doing for profiling?
Attached the tests.
#68 Updated by Alexandru Lungu almost 2 years ago
So you are testing the case when records are found? This is only checking the lambda removal. I think you should have some tests where a "not found" exception is actually thrown and see that lenientOffEnd is faster than exception throwing.
#69 Updated by Alexandru Lungu almost 2 years ago
TBD: the times you are seeing are severely spoiled by the actual database look-up that is done in order to find the record. Also, some queries are subject to fast-find cache. I suggest working with an empty table and avoid the build-up of ffcache (doing 10/100/1000 finds). After 100 finds, the first 100 finds from the total of 1000 will be cached already.
#70 Updated by Dănuț Filimon almost 2 years ago
- Status changed from Review to WIP
Alexandru Lungu wrote:
TBD: the times you are seeing are severely spoiled by the actual database look-up that is done in order to find the record. Also, some queries are subject to fast-find cache. I suggest working with an empty table and avoid the build-up of ffcache (doing 10/100/1000 finds). After 100 finds, the first 100 finds from the total of 1000 will be cached already.
Thanks, will do! Right now I noticed I missed something based on your review (#9032-65) so I need to rework the implementation for the AdaptiveFind since it will not work properly.
#71 Updated by Dănuț Filimon almost 2 years ago
- % Done changed from 80 to 100
- Status changed from WIP to Internal Test
Committed 9032b/15383. Addressed the #9032-65 review.
#72 Updated by Alexandru Lungu almost 2 years ago
This looks right! I am OK with the changes.
#73 Updated by Dănuț Filimon almost 2 years ago
- without 9032b See results
- with 9032b See results
- tt 250000 FIND CURRENT: With 9032b, the cold results matches the warm run results when not using 9032b;
- tt 100000 FIND FIRST and tt 250000 FIND FIRST improved;
- tt 100000 FIND NEXT and tt 250000 FIND NEXT are slower with 9032b;
- tt 100000 FIND PREV and tt 250000 FIND PREV are slower with 9032b;
- tt 100000 FIND LAST cold run improved, but not the same can be said about tt 250000 FIND LAST
- tt 100000 FIND improved;
- pt 100000 FIND CURRENT and pt 250000 FIND CURRENT improved;
- pt 250000 FIND FIRST one war run shows a worse result;
- pt 100000 FIND LAST and pt 250000 FIND LAST cold run improvements;
- pt 100000 FIND cold run improvement, but slower warm runs;
#74 Updated by Constantin Asofiei almost 2 years ago
addExternalBuffers in FindQuery need to return FindQuery, as a FIND ... WHERE CAN-FIND() will emit an addExternalBuffers and the call chain will be like new FindQuery().addExternalBuffers().silentFirst() - this fails javac compile, as RandomAccessQuery.addExternalBuffers() returns P2JQuery.
#75 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
addExternalBuffersin FindQuery need to return FindQuery, as aFIND ... WHERE CAN-FIND()will emit anaddExternalBuffersand the call chain will be likenew FindQuery().addExternalBuffers().silentFirst()- this fails javac compile, asRandomAccessQuery.addExternalBuffers()returnsP2JQuery.
Thank you! I've fixed this in 9032b/15384, the code compiles now after a small test. I've also tried to reproduce this with AdaptiveFind, but the same where clause is actually used in the initialize() method so the same does not apply to the AdaptiveFindQuery.
#76 Updated by Dănuț Filimon almost 2 years ago
Rebased 9032b to trunk/15393, the branch is now at revision 15399.
#77 Updated by Dănuț Filimon almost 2 years ago
Good news, I tested a hotel_gui and a customer application conversion and both were successful.
#78 Updated by Constantin Asofiei almost 2 years ago
The #8363 app converted and compiled OK, and performance run showed a small improvement. I need to reconvert ETF and run with it.
#79 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
Good news, I tested a hotel_gui and a customer application conversion and both were successful.
Smoke tests went well, but when I did a test on the customer unit tests it went into an infinite loop after 2 modules, while testing without any changes (old conversion) also showed an infinite loop in a different module. I am not sure if this customer has a spreadsheet with the results as the one from #8784-1.
#80 Updated by Constantin Asofiei almost 2 years ago
There are conversion regressions for this construct:
DO PRESELECT EACH tt1 WHERE tt1.f1 > 0:: FIND NEXT tt1 NO-ERROR. END.
This emits a silentNext() which is not defined for PreselectQuery.
The point: a FIND NEXT insinde a {DO|PRESELECT} PRESELECT block is not a FindQuery. We either move the silentNext/First/etc from FindQuery up to AbstractQuery, or change conversion rules to exclude these cases. I'd go with the first proposal, change runtime.
#81 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
There are conversion regressions for this construct:
[...]This emits a
silentNext()which is not defined forPreselectQuery.The point: a
FIND NEXTinsinde a{DO|PRESELECT} PRESELECTblock is not a FindQuery. We either move thesilentNext/First/etcfromFindQueryup toAbstractQuery, or change conversion rules to exclude these cases. I'd go with the first proposal, change runtime.
This is a problem... In RandomAccessQuery I defined silentFirst(), silentFirst(Object[]), silentFirst(LockType), silentFirst(Object[], LockType), the first three calling the last one. If we move those methods, we will not have a match for the PrelesectQuery as there is no first method that takes two parameters.
#82 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
This is a problem... In RandomAccessQuery I defined
silentFirst(), silentFirst(Object[]), silentFirst(LockType), silentFirst(Object[], LockType), the first three calling the last one. If we move those methods, we will not have a match for the PrelesectQuery as there is nofirstmethod that takes two parameters.
Actually this can work, but we will have to remove the silentFirst() and silentFirst(LockType) methods from RandomAccessQuery and AdaptiveFind, while silentFirst(Object[]) and silentFirst(Object[], LockType) will still be needed and not moved to AbstractQuery. Another nice change would be moving the errorHelper property to the AbstractQuery and using a method to initialize and use it (this will help avoid any future problems where errorHelper would be needed in the AbstractQuery and not be removed from AdaptiveFindQuery/RandomAccessQuery by mistake).
#83 Updated by Lorian Sandu almost 2 years ago
I tested 9032b with customer unit tests and there were no regressions.
#84 Updated by Dănuț Filimon almost 2 years ago
- Status changed from Internal Test to Review
Committed 9032b/15400. Support silent mode methods in AbstractQuery.
EDIT: Only runtime changes.
#85 Updated by Radu Apetrii almost 2 years ago
Just to be sure, 9032b needs to be conversion tested, and not 9032a, right?
#86 Updated by Constantin Asofiei almost 2 years ago
Radu Apetrii wrote:
Just to be sure, 9032b needs to be conversion tested, and not 9032a, right?
Yes.
#87 Updated by Constantin Asofiei almost 2 years ago
Conversion was OK with 9032b for the project.
#88 Updated by Dănuț Filimon almost 2 years ago
I managed to convert the regression xfer project, but had problems with the compilation (unrelated to the changes) which I reported in #7825-40.
#89 Updated by Radu Apetrii almost 2 years ago
Conversion went fine with 9032b for the project that I tested.
#90 Updated by Constantin Asofiei almost 2 years ago
ETF testing also passed.
#91 Updated by Dănuț Filimon almost 2 years ago
- Status changed from Review to Internal Test
#92 Updated by Dănuț Filimon almost 2 years ago
Created 9032c from trunk/15407. I'll use this branch to reduce the number of ErrorConditionException that are being thrown, especially for FIND .. NO-ERROR. I have a basic implementation down, but I want make sure that there are no issues (a customer test - 100 runs scenario) before adding the javadoc and committing it.
#93 Updated by Dănuț Filimon almost 2 years ago
| Number | Error Message | Count |
|---|---|---|
| 1 | ** <tt1> record not on file. (138) | 202226 |
| 2 | Cannot delete a BY-REFERENCE PARAMETER dataset or table in the called procedure. (12327) | 97383 |
| 3 | ** <tt2> record not on file. (138) | 92808 |
| 4 | ** <tt3> record not on file. (138) | 86646 |
| 5 | ** <tt4> record not on file. (138) | 85645 |
| 6 | ** <tt5> record not on file. (138) | 83447 |
| 7 | ** <tt6> record not on file. (138) | 72408 |
| 8 | ** FIND FIRST/LAST failed for table <tt7>. (565) | 63443 |
| 9 | Invalid or inappropiate handle value given to DELETE OBJECT or DELETE PROCEDURE statement. (5425) | 56073 |
| 10 | ** <tt8> record not on file. (138) | 53528 |
| 11 | ** <tt9> record not on file. (138) | 51573 |
| 12 | ** <tt10> record not on file. (138) | 41616 |
| 13 | ** FIND FIRST/LAST failed for table <tt11>. (565) | 34374 |
| 14 | ** FIND FIRST/LAST failed for table <tt12>. (565) | 32825 |
| 15 | ** <tt13> record not on file. (138) | 27068 |
Note that only a percentage of those are using silent mode (tested without any changes from 9032a or 9032b).
#94 Updated by Dănuț Filimon almost 2 years ago
Committed 9032c/15408. Suppress 138 and 565 4GL exceptions in silent mode, those exceptions will be available through the WorkArea of the ErrorManager and checked for the force flag (similar to what it is done in the catch block of the silent worker).
#95 Updated by Dănuț Filimon almost 2 years ago
As I am unable to finish building the regression xfer project due to #9032-88. What are the remaining tests that should be done for 9032b?
#96 Updated by Constantin Asofiei almost 2 years ago
9032b can be merged to trunk now
#97 Updated by Dănuț Filimon almost 2 years ago
I will be merging it soon, ~5-10 min.
#98 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
I will be merging it soon, ~5-10 min.
Nevermind, it will take a bit more. I get
brz: ERROR: Key b'osresource-20240821155930-wbmwcxlkbxi24t8y-1' is already present in mapwhen I try to rebase.
#99 Updated by Constantin Asofiei almost 2 years ago
Dănuț Filimon wrote:
Dănuț Filimon wrote:
I will be merging it soon, ~5-10 min.
Nevermind, it will take a bit more. I get
[...] when I try to rebase.
Do this from devsrv01.
#100 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
Do this from devsrv01.
Do I have to checkout the branches there and do it?
#101 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
Constantin Asofiei wrote:
Do this from devsrv01.
Do I have to checkout the branches there and do it?
It still does not work. I also did #7143-1175.
#102 Updated by Constantin Asofiei almost 2 years ago
Where did you checkout trunk? You need to have a checkout of trunk and a checkout of 9032b, and after that rebase on the trunk checkout.
#103 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
Where did you checkout trunk? You need to have a checkout of trunk and a checkout of 9032b, and after that rebase on the trunk checkout.
Radu made things clear for me and I am now checking out both 9032b and trunk on devsrc01 /home/ddf/branches/.
#104 Updated by Dănuț Filimon almost 2 years ago
Branch 9032b has been merged intro trunk/15411 and archived.
#105 Updated by Dănuț Filimon almost 2 years ago
- % Done changed from 100 to 50
- Status changed from Internal Test to WIP
I am putting this in WIP for 9032c at 50%, I still want to investigate other common errors as the ones mentioned in #9032-93 where the same change can be done for exceptions like 138 and 565.
#106 Updated by Dănuț Filimon almost 2 years ago
Dănuț Filimon wrote:
I am putting this in WIP for 9032c at 50%, I still want to investigate other common errors as the ones mentioned in #9032-93 where the same change can be done for exceptions like 138 and 565.
The rest of the implementation for 9032c right now is influenced by the changes from 9032b which made it into trunk/15411 so I will also have to address the changes after a rebase.
#107 Updated by Dănuț Filimon almost 2 years ago
Rebased 9032c to trunk/15411, the branch is now at revision 15412.
#108 Updated by Dănuț Filimon almost 2 years ago
Committed 9032c/15413. Moved the silent mode method finally block into a separate method.
#109 Updated by Dănuț Filimon almost 2 years ago
Committed 9032c/15414. 9032c is now compatible with the 9032b implementation that was merged in trunk/15411.
#110 Updated by Dănuț Filimon almost 2 years ago
- % Done changed from 50 to 100
- Status changed from WIP to Review
For review 9032c/15412-4.
I've done a small test with 9032c/15412 on a customer scenario (100 runs) and I noticed a 200ms improvement (400ms for the first run). I'd like to take this up on the bare metal instance and test the same scenario to confirm my findings.
#111 Updated by Dănuț Filimon almost 2 years ago
Constantin, it seems that there's a problem with the FindQuery constructor. There's the possibility of it throwing an exception which is no longer accounted in the silent operation. Lorian reported this to me after he rebased 9095a and retested his changes.
#112 Updated by Constantin Asofiei almost 2 years ago
Dănuț Filimon wrote:
Constantin, it seems that there's a problem with the FindQuery constructor. There's the possibility of it throwing an exception which is no longer accounted in the silent operation. Lorian reported this to me after he rebased 9095a and retested his changes.
What is the stacktrace?
#113 Updated by Dănuț Filimon almost 2 years ago
There's no stack trace, thrown errors will get displayed on the screen when they should not (because they were silent). I can provide the example Lorian gave me.
#114 Updated by Constantin Asofiei almost 2 years ago
Dănuț Filimon wrote:
There's no stack trace, thrown errors will get displayed on the screen when they should not (because they were silent). I can provide the example Lorian gave me.
Well, find where the error is thrown and capture it in the debugger. Plus a standalone test will help.
#115 Updated by Dănuț Filimon almost 2 years ago
Note that this is only visible with 9095a changes, the patch:
=== modified file 'src/com/goldencode/p2j/persist/FQLPreprocessor.java'
--- old/src/com/goldencode/p2j/persist/FQLPreprocessor.java 2024-07-03 14:06:51 +0000
+++ new/src/com/goldencode/p2j/persist/FQLPreprocessor.java 2024-09-02 10:49:58 +0000
@@ -405,6 +405,7 @@
import com.goldencode.p2j.persist.dialect.*;
import com.goldencode.p2j.persist.hql.*;
import com.goldencode.p2j.persist.pl.*;
+import com.goldencode.p2j.util.ErrorManager;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.logging.CentralLogger;
@@ -5180,6 +5181,12 @@
Aast alias = parent != null && parent.getType() == ALIAS ? parent : null;
RecordBuffer rbuff = lookupBuffer(alias);
String denormalizedProperty = TableMapper.getDenormalizedProperty(rbuff, ast.getText(), extentIndex);
+
+ if (denormalizedProperty == null)
+ {
+ ErrorManager.recordOrThrowError(26, String.valueOf(extentIndex), "");
+ }
+
ast.setText(denormalizedProperty);
ast.removeChildren();
ast.putAnnotation("inlined", Boolean.TRUE);
start.p:
create test_table.
test_table.f1[1] = 10.
release test_table.
def var iPos as int.
def var cAlphabet as char init "ABCDEFGHIJKLMNOPQRSTUVWXYZ" no-undo.
def var cChar as char.
cChar = '1'.
iPos = index(cAlphabet, cChar, 1).
def var i as int.
function test int:
find first test_table where f1[iPos] ne 0 no-error.
if (error-status:num-messages > 0) then do:
message error-status:error error-status:num-messages.
end.
message "test method called".
return 1.
end.
find first test_table where test () eq 1 no-lock no-error.
message "executed".
fwd.df
ADD TABLE "test_table" AREA "Schema Area" DUMP-NAME "test_table" ADD FIELD "f1" OF "test_table" AS integer FORMAT "->>,>>9" INITIAL "0" POSITION 2 MAX-WIDTH 312 EXTENT 26 ORDER 10 . PSC cpstream=ISO8859-1 . 0000000214
Error is displayed by the added error from the patch, FQLPreprocessor:5187.
#116 Updated by Dănuț Filimon almost 2 years ago
I am also trying to find a test case that does not require this patch.
#117 Updated by Constantin Asofiei almost 2 years ago
The constructor for FindQuery looks like this:
public FindQuery(DataModelObject dmo,
String where,
Supplier<logical> whereExpr,
String sort,
DataModelObject inverse,
Object[] args,
LockType lockType)
{
super();
super.initialize(dmo, where, whereExpr, sort, null, inverse, args, lockType);
// if the NO-ERROR clause is specified, then a QOEE must not be thrown
setLenientOffEnd(getBuffer().txHelper.errHlp.isSilent());
setErrorIfNull(true);
}
The super.initialize and other code needs to be executed in the find and silentFind versions, to be aware of the new 'silent' mode for FIND.
#118 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
The
super.initializeand other code needs to be executed in thefindandsilentFindversions, to be aware of the new 'silent' mode for FIND.
That's actually a better idea that what I was thinking. This should go on a separate branch (9032d) so I'll start working on it asap.
#119 Updated by Dănuț Filimon almost 2 years ago
- % Done changed from 100 to 50
- Status changed from Review to WIP
Put into WIP to solve the issue reported in #9032-111.
#120 Updated by Dănuț Filimon almost 2 years ago
- Status changed from WIP to Review
#121 Updated by Constantin Asofiei almost 2 years ago
This code:
// if the NO-ERROR clause is specified, then a QOEE must not be thrown
setLenientOffEnd(getBuffer().txHelper.errHlp.isSilent());
is no longer included in
initializeQuery.#122 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
This code:
[...]
is no longer included ininitializeQuery.
It is not needed anymore, it is either false for non-silent operations or set directly in the silent mode method.
#123 Updated by Constantin Asofiei almost 2 years ago
Thanks, I see it now. But I think the same can be achieved by overriding the first/last/etc methods in FindQuery, instead of using an empty default method initializeQuery in P2JQuery. It can get confusing that there is no impl for RAQ, and this is only for FIND anyway.
#124 Updated by Dănuț Filimon almost 2 years ago
- Status changed from Review to Internal Test
Constantin Asofiei wrote:
Thanks, I see it now. But I think the same can be achieved by overriding the
first/last/etcmethods inFindQuery, instead of using an empty default methodinitializeQueryinP2JQuery. It can get confusing that there is no impl for RAQ, and this is only for FIND anyway.
Committed 9032d/15413. Added overrides for find methods and reverted the changed from P2JQuery and RandomAccessQuery.
#125 Updated by Constantin Asofiei almost 2 years ago
I think 9032d is good, we'll need to run a ETF test.
#126 Updated by Constantin Asofiei almost 2 years ago
- Status changed from Internal Test to WIP
The changes are not complete. All unique/first/last/etc methods from RAQ need to be overridden. For example there is this:
public boolean unique()
{
return unique(getCurrentArgs(), lockType);
}
which calls
getCurrentArgs() and this needs to have initializeQuery already executed.#127 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
The changes are not complete. All
unique/first/last/etcmethods from RAQ need to be overridden. For example there is this:
[...]
which callsgetCurrentArgs()and this needs to haveinitializeQueryalready executed.
This is now fixed in 9032d/15414.
#128 Updated by Dănuț Filimon almost 2 years ago
hasAny() calls also need to initialize the query.
#129 Updated by Constantin Asofiei almost 2 years ago
Dănuț Filimon wrote:
hasAny() calls also need to initialize the query.
Also hasOne(). Any method emitted for FindQuery in conversion needs to have this.
#130 Updated by Dănuț Filimon almost 2 years ago
Constantin Asofiei wrote:
Dănuț Filimon wrote:
hasAny() calls also need to initialize the query.
Also hasOne(). Any method emitted for FindQuery in conversion needs to have this.
Yes, this also includes addExternalBuffers(). I was trying to look into the customer project I converted with 9032b, but I accidentally ran an ant deploy.all while I actually wanted to reimport the database.
Committed 9032d/15415.
#131 Updated by Constantin Asofiei almost 2 years ago
ETF testing passed with 9032d/15415. Please do some basic testing of some other customer apps.
#132 Updated by Dănuț Filimon almost 2 years ago
I smoke tested a customer application and found no issues.
#133 Updated by Constantin Asofiei almost 2 years ago
Greg, 9032d can be queued for merge.
#134 Updated by Constantin Asofiei almost 2 years ago
Constantin Asofiei wrote:
Greg, 9032d can be queued for merge.
Please merge to trunk now.
#135 Updated by Dănuț Filimon almost 2 years ago
Branch 9032d was merged to trunk rev 15416 and archived.
#136 Updated by Roger Borrello almost 2 years ago
Dănuț Filimon wrote:
Branch 9032d was merged to trunk rev 15416 and archived.
Does this address the error: code too large issue noted in #8860-66?
#137 Updated by Alexandru Lungu almost 2 years ago
Danut is out-of-office until Monday.
Anyway, I don't think that #9032 itself will solve anything from "code too large". There is a 64kB byte limit per method. Danut's changes is basically generating (a bit) more byte-code for each FIND query.
AFAIK, FWD doesn't have anything like "automatically split code to satisfy Java constraints", so this will require changes in 4GL code: maybe split a procedure in different internal procedures? something like that.
#138 Updated by Greg Shah almost 2 years ago
Roger Borrello wrote:
Dănuț Filimon wrote:
Branch 9032d was merged to trunk rev 15416 and archived.
Does this address the error: code too large issue noted in #8860-66?
There is NOT a solution in FWD for this. See Code Too Large javac issue. The bottom line is that this code must be refactored on the 4GL side to split up the code into multiple procedures so that the individual methods are smaller than today.
This issue can appear at any time that we make changes to the conversion and some methods will just be "on the edge" of the problem (e.g. #8860-65). Something in the changes pushed that case "over the edge" and now some 4GL changes are needed. We don't see this issue in every project but it can happen a few times in any given project.
#139 Updated by Dănuț Filimon almost 2 years ago
It has come to my attention that there is an issue with lenientOffEnd not being set for silent(() -> FindQuery().next()) calls. After 9032d was merged, the flag is no longer set in the constructor and this is done by the silent method so the runtime is no longer compatible with old conversions. Anything converted with trunk/15411 will require trunk/15416 to work, while any conversion that uses a revision older than 15411 will be compatible with the runtime up to 15415.
#140 Updated by Dănuț Filimon almost 2 years ago
Rebased 9032a to latest trunk/15472, the branch is now at revision 15476.
Rebased 9032c to latest trunk/15472, the branch is now at revision 15473.