Bug #7425
Avoid directory usage in static blocks
100%
Related issues
History
#1 Updated by Alexandru Lungu about 3 years ago
After discussing with Constantin, mostly in regard to #7398, there are several places in FWD where the directory is read from static blocks. This reading is done in a quite non-deterministic fashion to us. In the backstage, the read is done when the class is loaded, but this is not quite easy to pinpoint and certainly not easy to manage.
A solution here is to refactor static blocks into static initializing methods. These methods will be called at server bootstrap, such that the directory read is done at a deterministic point. An initial effort in this sense is done in #7388, where cache sizes will be configured only once at the server bootstrap based on directory.xml values.
This work is not trivial, as some class initialization don't make sense (i.e. TableHints.initialize, TempTableDataSourceProvider.initialize - these are not a static helper classes, .initialize doesn't seem to make sense). For caches, we will intentionally create a separate static helper class whose .initialize makes sense. I would expect to see the same for other classes (maybe move the directory.xml reads in other helper classes which already have .initialize). We need to avoid calling tens of .initialize at server bootstrap, but to group them logically.
#3 Updated by Alexandru Lungu about 3 years ago
- Related to Bug #7388: Create server configuration container for cache sizes added
#4 Updated by Alexandru Lungu about 3 years ago
- Status changed from New to WIP
- Assignee set to Dănuț Filimon
Danut, this is close to your work on #7388. I will do the review on caches there asap.
Please extend your research on other static initializers we have in place. For the caches in #7388, you've done some extra work which is not necessarily required for other static blocks. Consider just renaming the static blocks into initialize static methods and call them at server bootstrap. Make the class members non-final if customizable from directory.xml. Initialize the values with the default until the bootstrap (to cover conversion-side). Skip code in static blocks which is not related to directory.xml
Before this whole purging process, please post a list here of all such static blocks you find across FWD.
Created 7425a.
#5 Updated by Dănuț Filimon about 3 years ago
directory.xml:
TemporaryAccountPoolWebHandler: from the main packageDirtyShareSupportP2JQueryLoggerSession: from the persist.orm packageSQLStatementLoggerTempTableDataSourceProvider: Note that the static block was removed in 7388a.DatabaseManagerDatabaseStatisticsMetafileHelperXprHelperJavaTypeSerializerTableHintsEnvironmentOps
I will double check after replacing the current static blocks with initialize methods to make sure that none are missed. If there are places where the current directory.xml access should not be removed, tell me.
EDIT: SQLStatementLogger doesn't have a static initializer block.
#6 Updated by Alexandru Lungu about 3 years ago
Danut, try to make the initialization calls hierarchical. That is, do not list all initialization in StandardServer.
For example, I think P2JQueryLogger, DatabaseStatistics and SQLStatementLogger can be grouped. In fact P2JQueryLogger and SQLStatementLogger can be initialized from DatabaseStatistics.
#7 Updated by Dănuț Filimon about 3 years ago
initialize method, so to avoid confusion I bootstrap for the method name. Currently I modified classes from the main, persist, reporting, serializers, schema and utils and looked into initializing in a hierarchical order:
P2JQueryLoggercan be initialized fromDatabaseStatistics, as previously mentioned by Alexandru- Any class from the
persistpackage can be initialized inPersistence.initialize - There are already server hooks defined for
TemporaryAccountPooland supposedlyWebHandler(correct me if I am wrong) - Created new server hooks for the rest of the
bootstrapcalls, grouped by package.
All the changes that I am going make here should be committed to 7388a instead of 7425a because the server configuration for cache initialization was changed.
I committed 7388a/rev.14620 where static initializers were replaced by methods called at server bootstrap. I tested the changes on small programs, but will continue with testing a customer application.
Constantin, could you review these changes?
#8 Updated by Alexandru Lungu about 3 years ago
- % Done changed from 0 to 50
This bootstrap is triggered only on server start-up from StandardServer hooks. Should we consider other FWD usages when bootstrapping? My knowledge is that directory.xml is used exclusively with SERVER mode. Therefore, conversion should stick with the default values, not configurable by any directory.xml.
#9 Updated by Alexandru Lungu about 3 years ago
Rebased 7388a from trunk rev. 14633. 7388a is now at rev. 14636.
#10 Updated by Alexandru Lungu about 3 years ago
Review of 7388a
This also includes the review for #7388.- In
StandardServer, you no longer needDatabaseStatistics.getas the bootstrap is done explicitly already. You can remove the get call. Move the comment before.bootstrap - I think we should have a more uniform way of handling directory binding. Please decide only one single pattern for all
- there are places where
Utils.getDirectoryNodeInt(null, ...);is used straight-away (this is OK only when the target instance can be null) - there are places where
if (!dir.bind()) throw new RuntimeException("Directory bind failed");is used to check if there is a bind directory. (this should be used) - there are places where
finally{ ds.unbind(); }is used after usage: sometimes this doesn't happen; sometimes the unbind is not in a finally clause (this should be used) - there are places where
ds.bind()happens in the try clause withds.unbind()finally clause; some have thebindoutsidetry(It doesn't make sense to attempt unbinding if binding failed) - there are places where
ds == nullis used (I think this is decent to be used)
- there are places where
- Private constructor in
private P2JQueryLogger()was removed (?) - Adding public in
public class Persisteris something dangerous from my POV. Maybe we can bootstrap it from another class in the same package without exposingPersister. CacheManagerhas several issues:initializeCachecalls may be omitted if we return early or an exception is thrown (the directory is not bound). The initialize should be always done, no matter if we return early / an exception is thrown.- The logs are not very descriptive. Please include the name of the parameters and values to identify what is wrong. If a class could not be found, log the name of it. If a cache is misconfigured, log its name.
- I don't think it is safe to use
initializein a multi-session environment. Please bootstrap CacheManager only once at server start-up to ensure it is initialized only once. Remove anyif (!initialized) initialize();code. - Please make
ClassKey.equalsreturn false ifois not an instance ofClassKey
- Move
Session.PKafter all final members, as it is no longer final. There are other cases of such mismatch (e.g.DatabaseManager) Session.createCacheno longer includes the database name in the cache name. Consider usingdatabase.toString()as discriminator. Change your cache size look-up to the following. This allows setting different cache sizes for each database sessions.- If you require class and discriminator: If there is a (class, discriminator) entry use it. Otherwise if there is a (class, null) entry use it. Lastly, use the default.
- If you require class: If there is a (class, null) entry use it. Lastly, use the default.
- Please fix history entry numbers: everything that comes from 7388 should share the same history number / per file (e.g.
BufferManager). - Anything that you made non-final should be private and retrieved through getters. Otherwise, we may change it from outside of the class.
- Please do a check with a large customer application. Debug a bit to check if the values are right considering the directory.
Danut, please fix this ASAP.
#11 Updated by Dănuț Filimon about 3 years ago
- Removed the
DatabaseStatistics.getcall inStandardServer - This will be the standard for handling directory binding:
if (ds != null) { if (!ds.bind) { throw new RuntimeException("Directory bind failed"); } try { } finally { ds.unbind(); } } - I instantiated the
Persistercache inSession.bootstrap()and removed thepublicmodifier - Added better log messager in
CacheManager - Fixed
initializeCachecalls that could be omitted if binding fails. - Removed
intializedand it's usage inCacheManager, even though caches can be instantiated beforeCacheManager.initialize()is called, a default value is used for the cache size - Added an additional check for
ClassKey.equalsmethod - Reordered class members in some cases
- Fixed history entries
- Added
CacheManager.getCacheSizethat will be used to look for the cache size of a cache. If a discriminator is provided but there is no value found, it will use the default key (discriminator isnull) to look again for the cache size.
Alexandru Lungu wrote:
- Private constructor in
private P2JQueryLogger()was removed (?)
It was not removed.
- Anything that you made non-final should be private and retrieved through getters. Otherwise, we may change it from outside of the class.
It is really necessary to use getters when the cache is not used outside of the class? I think you are talking about DatabaseManager.forceNoUndoTempTables. In this case I added getters for this member. Same goes for DirtyShareSupport class members.
Note that if that's the case, should I still make these changes to Session.PK and JavaTypeSerializer members? I didn't do it yet because they are used in a lot of places and I would like to do a separate commit.
I tested a large customer application and everything worked well, but I did make a few more changes and need to retest to make sure.
Here is an example from directory.xml on how to set up a cache size:
<node class="container" name="08">
<node class="string" name="class-name">
<node-attribute name="value" value="com.goldencode.p2j.persist.FastFindCache"/>
</node>
<node class="integer" name="size">
<node-attribute name="value" value="10"/>
</node>
<node class="string" name="discriminator">
<node-attribute name="value" value="L2"/>
</node>
</node>
Note that the discriminator is optional.#12 Updated by Dănuț Filimon about 3 years ago
I retested the customer application and found out that the psCache is initialized before the CacheManager, which means that the cache will not use the configured size. Only after the context is closed and a new one is opened, the psCache will use the configured size. I've committed 7388a/rev.14638 which solves this issue by initializing CacheManager before DatabaseManager. Except for this problem, I had no other issues.
#13 Updated by Alexandru Lungu about 3 years ago
Radu, please check P2JQueryLogger. I feel like I've mismatched something in it while rebasing. More specific, I am not sure if loggingEnabled is still used to rule out logging. Please double-check 7388a, more specifically your logging classes.
#14 Updated by Radu Apetrii about 3 years ago
Alexandru Lungu wrote:
Radu, please check
P2JQueryLogger. I feel like I've mismatched something in it while rebasing. More specific, I am not sure ifloggingEnabledis still used to rule out logging. Please double-check 7388a, more specifically your logging classes.
A short answer to this would be that loggingEnabled is still used to mark whether P2JQuery logging is enabled or not. I don't see anything being out of place.
- You can enable/disable P2J query logging in
directory.xml, under thepersistencetag, by having:<node class="container" name="p2j-query-logging"> <node class="boolean" name="enabled"> <node-attribute name="value" value="TRUE"/> </node> ... </node> P2JQueryLoggerdoes not support yet an output file to be set, meaning that logs will be placed in the server log, as aWARNING. An example of aRandomAccessQuery(without stack trace) log is:23/06/28 15:37:50.557+0300 | WARNING | com.goldencode.p2j.persist.orm.P2JQueryLogger | ThreadName:Conversation [00000002:bogus], Session:00000002, Thread:00000013, User:bogus | com.goldencode.p2j.persist.RandomAccessQuery: Records processed: 1;- Although there is a field called
loggingEnabledinP2JQueryLogger, it is not actually used, becauseP2JQueryExecutoris the one that controls the situation. TheloggingEnabledfield that should be used is the one fromP2JQueryExecutorand not fromP2JQueryLogger. The latter one is redundant, I forgot to delete it.
I'm not sure if this answers your question, but the point is that I don't see anything wrong.
#15 Updated by Alexandru Lungu about 3 years ago
Although there is a field called loggingEnabled in P2JQueryLogger, it is not actually used, because P2JQueryExecutor is the one that controls the situation. The loggingEnabled field that should be used is the one from P2JQueryExecutor and not from P2JQueryLogger. The latter one is redundant, I forgot to delete it.
This is what confused me in the rebase process. I thought that maybe I missed the usage of loggingEnabled in P2JQueryLogger. Thank you for the clarification. Please delete it here in 7388a.
#16 Updated by Radu Apetrii about 3 years ago
Alexandru Lungu wrote:
Although there is a field called loggingEnabled in P2JQueryLogger, it is not actually used, because P2JQueryExecutor is the one that controls the situation. The loggingEnabled field that should be used is the one from P2JQueryExecutor and not from P2JQueryLogger. The latter one is redundant, I forgot to delete it.
This is what confused me in the rebase process. I thought that maybe I missed the usage of
loggingEnabledinP2JQueryLogger. Thank you for the clarification. Please delete it here in 7388a.
Done, committed to 7388a, rev. 14639. Sorry for the confusion!
#17 Updated by Alexandru Lungu about 3 years ago
- Status changed from WIP to Review
- % Done changed from 50 to 100
#18 Updated by Alexandru Lungu about 3 years ago
Danut, please let me know if there is something else to be addressed here. I will like to do a review and testing round. If everything is right, we shall commit this asap.
#19 Updated by Dănuț Filimon about 3 years ago
#20 Updated by Alexandru Lungu about 3 years ago
Review of 7388a/rev. 14639
TemporaryAccountPoolstill has a static block that does all kind of initialization. Should we consider moving all that inbootstrapinstead of leaving them in thestaticblock? I mean,ServerStandardwas forcing this static block anyway withgetInstance, so from my POV, these can be safely moved inbootstrap. I am not aware if this class should be initialized outside the server. Maybe somebody with moreTemporaryAccountPoolbackground can advice.- This is a place where the
directory.xmlpattern is not honored: look-up fords, check for null and bind, etc.
- This is a place where the
dsandbindare not used forWebHandler- I missed checking the other files to for this pattern, please do a second check here
I am OK with other changes.
Constantin, can you do a second review here, please? I think this was supposed to reach trunk already (some time ago), but even so, I guess it will be OK if we get it now asap.
#21 Updated by Dănuț Filimon about 3 years ago
Alexandru, you previously mentioned that "there are places where Utils.getDirectoryNodeInt(null, ...); is used straight-away (this is OK only when the target instance can be null)" so I considered that the directory service change in this case is not required. In both cases, TemporaryAccountPool and WebHandler use null and the directory service is called by Utils.doWithBoundsDs. From my point of view, it looks a lot cleaner.
#22 Updated by Alexandru Lungu about 3 years ago
You are right Danut, my bad on this one. It is fine to keep null for ds and let the directory service do the work. I am just obsessed of keeping bind/unbind pattern balanced. For null, the balance is ok behind the scenes. For other places, I want to stress out how important is to keep bind/unbind ref count OK.
#23 Updated by Constantin Asofiei about 3 years ago
Alexandru, I guess the branch is 7388a, with latest rev 14639. I'm reviewing this now.
#24 Updated by Alexandru Lungu about 3 years ago
Right, 7388a was the one I meant to write. Edited now.
#25 Updated by Constantin Asofiei about 3 years ago
TemporaryDatabaseManager.initialize- unbalancedds.bind, this is an existing issue, please fix it.- other cases which have
Utils.getDirectoryNodein static constructor and should be initialized at server startup, if not already done implicitly by loading the class:P2JQueryExecutor- static c'torValidation.P4GL_MAX_INDEX_SIZE- static fieldPersistence.foreignKeysEnabled- static field initialized, but I think this is OK, asPersistenceclass gets initialized at FWD server startup AFAIK.HtmlBrowserWidget- has static fields, but this is used only in GUI, so is OKFileSystemOps.pkgRootPath- static fieldSecurityOps.compatible- static field
FQLPreprocessor- please leave only 096 number and remove 097 text.orm.Session- same, remove 007 text from history entry
Otherwise, nice work, Danut.
#26 Updated by Dănuț Filimon about 3 years ago
- Fields were made non-final and renamed if necessary (uppercase to camel case), the only exception being
P4GL_MAX_INDEX_SIZE; - As suggested, I skipped
Persistence.foreignKeysEnabledandHtmlBrowserWidget; - Directory handling was brought up to the standard in
TemporaryDatabaseManagerandP2JQueryExecutoras mentioned in #7425-11.
#27 Updated by Alexandru Lungu about 3 years ago
I will do a second review asap. I am intending to do a second rebase + test + profile just to check that the performance isn't degraded. If everything is ok, can we merge this by tomorrow EOD?
#28 Updated by Greg Shah about 3 years ago
If everything is ok, can we merge this by tomorrow EOD?
Yes
#29 Updated by Alexandru Lungu about 3 years ago
Review:
- I am still a bit concerned of
TemporaryAccountPool.getInstance(); TemporaryAccountPool.bootstrap();. I feel likebootstrapshould be enough, right? That implies moving the static block inbootstrap. Constantin, any feedback here forTemporaryAccountPool? - I am a bit concerned of the
createLRUCacheinterface that always requires a discriminator. This is usually a particularity of some caches; the common case is "no discriminator". Danut, please create a second method only with class and size, which calls this 3 parameter method under the hood with null as discriminator. Refactor the places wherenullis used as second parameter. - Be more consistent with cache sizes. This is not related with your changes, but can be fixed now:
Persistence.Context.cachehas size1000. Can we make this1024for the sake of consistency with other cache sizes?
I tested the changes and got no regression. Performance is consistent with my baseline - this is expected as most changes are related to server start-up anyway.
#30 Updated by Dănuț Filimon about 3 years ago
Alexandru Lungu wrote:
Danut, please create a second method only with class and size, which calls this 3 parameter method under the hood with null as discriminator. Refactor the places where
nullis used as second parameter.
Be more consistent with cache sizes. This is not related with your changes, but can be fixed now:Persistence.Context.cachehas size1000. Can we make this1024for the sake of consistency with other cache sizes?
I added the an additional createLRUCache and increased the cache size of the case in Persistence.Context to 1024. These changes were committed to 7388a/rev.14641. Here's a list of known classes with inconsistent cache sizes: BufferManager (10000), FastFindCache (L2 - 10 and L3 - 100).
#31 Updated by Constantin Asofiei about 3 years ago
Alexandru, TemporaryAccountPool is OK, it gets fully initialized at server startup. I don't think even bootstrap is needed, but you can leave it to be consistent.
#32 Updated by Alexandru Lungu about 3 years ago
Danut, please do the same changes to BufferManager and FastFindCache. Find the smallest power of 2 bigger than the existent sizes. I don't think these are going to break something (hopefully) - I will just do a very quick check before merge.
So, after you do the changes to BufferManager and FastFindCache, I will rebase and commit.
#33 Updated by Dănuț Filimon about 3 years ago
BufferManager: from 10000 to 16384.FastFindCacheL2: from 10 to 16.FastFindCacheL3: from 100 to 128.
Hope that BufferManager won't cause any problems, it's cache size went up by more than 50%.
#34 Updated by Alexandru Lungu about 3 years ago
Well, BufferManager is per-session so it may be a problem. We can go the other way around sticking to 8192 which is closer to the original value, but may cause performance draw-backs. For FFC, from my POV, they are quite right. I guess we can go with 16384, as long as we allow decreasing its size through server options, right? On the other way, I want to get 7388a in trunk in the next hour, so I may no be able to do the proper testing. Danut, I reverted the changes from #7425-33 as they may be too bold and may require extra testing. I will consider creating another task for cache size tuning.
#35 Updated by Alexandru Lungu about 3 years ago
- Status changed from Review to Test
Merged 7388a in trunk as rev. 14641 and archived.
#36 Updated by Dănuț Filimon about 3 years ago
I will update the Database Configuration wiki with the proper configuration and explanation for cache sizes as suggested in #7388-5.
#37 Updated by Dănuț Filimon about 3 years ago
I updated Database Configuration, this is subjected to changes since I am still working on it, but it should provide a basic explanation on how cache sizes should be configured.
#38 Updated by Alexandru Lungu about 3 years ago
Danut, please make sure you get this done correctly asap. I intend to experiment with some fine-tuning using this configuration.
#39 Updated by Dănuț Filimon about 3 years ago
Alexandru Lungu wrote:
Danut, please make sure you get this done correctly asap. I intend to experiment with some fine-tuning using this configuration.
I finished writing the wiki, please let me know what you think and if I missed something.
#40 Updated by Alexandru Lungu about 3 years ago
I am OK with the documentation. Looks clear to me. However, I feel you can do just a bit more feedback on the "list of caches". It is useful if you can provide their full class name (including package). One can simply check the "cache key" and configure it, without the need to actually search for that class inside the FWD code. Instead of FastFindCache, use com.goldencode.p2j.persist.FastFindCache. Also, please let the documentation hold the default cache sizes.
#41 Updated by Dănuț Filimon about 3 years ago
Alexandru Lungu wrote:
I am OK with the documentation. Looks clear to me. However, I feel you can do just a bit more feedback on the "list of caches". It is useful if you can provide their full class name (including package). One can simply check the "cache key" and configure it, without the need to actually search for that class inside the FWD code. Instead of
FastFindCache, usecom.goldencode.p2j.persist.FastFindCache. Also, please let the documentation hold the default cache sizes.
Done. Thank you for the feedback.
#42 Updated by Dănuț Filimon about 3 years ago
The order of the server bootstrap initialization was changed in trunk/rev.14654 and the resources affected need to be investigated further and decide if the current order is correct. For further information on this investigation, please refer to #7534-7.
- There were some instances where cache instantiation was moved to other initialization methods, such as
Persister.initializeCachewhich is called bySession.bootstrap. In this case,CacheManager.initializeshould be called beforeSession.bootstrapto make sure that the cache is created with the configured size. - From the list mentioned above, the following only read from the directory configuration:
Session,Validation,P2JQueryExecutor,DirtyShareSupport. DataTypeHelper.initializeinitializes static resources which don't make use of directory access / cache configurations / previously modified resources.TemporaryDatabaseManager.initializecreates an instance ofPrivateTempDbManager/SharedTempDbManager, which doesn't require previously modified resources.- The order between
TemporaryDatabaseManager,DatabaseManagerandP2OLookupshould not be changed.DatabaseManagerwill work with the private/shared manager that was created and thenP2OLookuprequires the managed databases that are created by it.
The conclusion is that CacheManager can be placed first, followed by Session, DirtyShareSupport, P2JQueryExecutor Validation, and DataTypeHelper. CacheManager also reads the configuration (with the exception of the 2 caches that are initialized by it).
I did not mention anything about SessionFactory.SessionCloseThread.initialize since it starts a thread before any SessionFactory is created and it worked properly.