Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TRUNK-6208: Upgrade 2.7 Platform to Liquibase 4.27.0 #4637

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

k4pran
Copy link
Contributor

@k4pran k4pran commented May 13, 2024

Description of what I changed

This upgrades liquibase to the latest version 4.27.0. There are few other changes worth mentioning

  • duplicateFileMode is a new config added to liquibase to handle duplicate changelogs. The only options at the minute is for it to error on any duplicates or do log a warning. I couldn't work around this config without overriding some of the lower level liquibase classes, so I opted to default it to WARN. The issue is that the warning logs are quite excessive. I raised this with liquibase and got the green light to add more options - Add Additional Duplicate File Modes (DEBUG and / or IGNORE) to ResourceAccessor liquibase/liquibase#5856
  • I wasn't 100% sure the best way to add duplicateFileMode as a config in openmrs, it seems that liquibase needs it set in the pom, a liquibase.properties file or an environment variable. It also needs set before liquibase runs, I opted to add a constant defaults to WARN with the option to override this either in the openmrs-runtime.properties or in the environment variables with the openmrs properties taking precedence.
  • The liquibase changelogs after 2.4.x were missing the logicalFilePath attribute, this resulted in an issue where ChangeLogDetective had difficulty determining the changelog used to initialize the database. I only added the missing attribute in 2.6.x and later. I could also add it to 2.5.x but didn't want to cause a compatibility issue doing so.
  • The md5sum generator in liquibase changed to version 9 which isn't compatible with version 3 that was used to calculate checksums in the H2 database used by integration tests. I got liquibase to recalculate the checksums for the update.xml files so you will see the H2 db object in the diff.
  • A change was also made in liquibase to determine if a type is an int or bigint. An integer with a display width greater than 10 e.g. int(11) is considered a bigint, this broke some tests that used the retired_by column in the program_attribute_table as hibernate has it set as an integer rather than a long, I added a changeset to reduce it from int(11) to int(10) to fix this.

I updated a few deprecated methods, there are some additional deprecation that I didn't touch, I can do it here or separately in case it is more involved:

  • OpenmrsClassLoaderResourceAccessor - most of this has been deprecated
  • FileSystemResourceAccessor - has been deprecated in favour of DirectoryResourceAccessor or ZipResourceAccessor. I think FileSystemResourceAccessor was handling both cases.

Issue I worked on

see https://issues.openmrs.org/browse/TRUNK-6208

Checklist: I completed these to help reviewers :)

  • My IDE is configured to follow the code style of this project.

    No? Unsure? -> configure your IDE, format the code and add the changes with git add . && git commit --amend

  • I have added tests to cover my changes. (If you refactored
    existing code that was well tested you do not have to add tests)

    No? -> write tests and add them to this commit git add . && git commit --amend

  • I ran mvn clean package right before creating this pull request and
    added all formatting changes to my commit.

    No? -> execute above command

  • All new and existing tests passed.

    No? -> figure out why and add the fix to your commit. It is your responsibility to make sure your code works.

  • My pull request is based on the latest changes of the master branch.

    No? Unsure? -> execute command git pull --rebase upstream master

Copy link

@njiddasalifu njiddasalifu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you notice the maven build failing

@k4pran
Copy link
Contributor Author

k4pran commented May 18, 2024

Did you notice the maven build failing

Yes, I am working on that. PR is currently in draft status and I will move out of draft once builds are passing. Thanks

@k4pran k4pran force-pushed the TRUNK-6208 branch 5 times, most recently from 9f6d767 to 46bf41c Compare May 19, 2024 20:43
@k4pran k4pran marked this pull request as ready for review May 19, 2024 20:43
@dkayiwa
Copy link
Member

dkayiwa commented May 19, 2024

@k4pran when i run these changes against an existing openmrs database, it takes me to the maintenance mode database setup wizard page with very many changes that are considered as have never been run before. Do you still have sometime to look into this? 😊

@k4pran
Copy link
Contributor Author

k4pran commented May 20, 2024

@k4pran when i run these changes against an existing openmrs database, it takes me to the maintenance mode database setup wizard page with very many changes that are considered as have never been run before. Do you still have sometime to look into this? 😊

I was able to reproduce this and the issue went away when I revert the logicalFilePath attributes I added to the xmls, I guess adding those doesn't work for backwards-compatibility. Not having them makes the changelogs more sensitive to where liquibase is being run, I guess since openmrs is running liquibase it might not be an issue, but if for some reason someone was to initialize the database with changelogs manually by running a liquibase CLI for example I think it could result in this kind of issue. For example since 2.5.x doesn't have this attribute it results in filenames like

org/openmrs/liquibase/updates/liquibase-update-to-latest-2.5.x.xml

instead of

liquibase-update-to-latest.xml

Please try again and let me know if you still have the issue, also let me know if you think we need to be concerned about someone running liquibase from a different location and I can see if there are alternative solutions.

@dkayiwa
Copy link
Member

dkayiwa commented May 20, 2024

@k4pran thank you so much for looking into this. Did you find a way for getting rid of the so many logs about the liquibase search path?

@k4pran
Copy link
Contributor Author

k4pran commented May 21, 2024

@k4pran thank you so much for looking into this. Did you find a way for getting rid of the so many logs about the liquibase search path?

I am working on adding options to ignore these warnings or log at debug level, but I need to do that in liquibase, haven’t found a way to ignore them without updating liquibase - liquibase/liquibase#5856
But that would be added in another release

EDIT: Actually I just had an idea, luckily the class that logs the warning only has a single log statement in it (the warning log about duplicate changelogs), so I could add a logging rule to set the log level for that class to error? Not a long term solution but might be ok until we make the changes in liquibase for the long term solution?

@dkayiwa
Copy link
Member

dkayiwa commented May 21, 2024

so I could add a logging rule to set the log level for that class to error? Not a long term solution but might be ok until we make the changes in liquibase for the long term solution?

I agree with you! 😊

@k4pran
Copy link
Contributor Author

k4pran commented May 22, 2024

so I could add a logging rule to set the log level for that class to error? Not a long term solution but might be ok until we make the changes in liquibase for the long term solution?

I agree with you! 😊

So another complication cropped up, setting the log level to ERROR for this class was fine, but I realised that liquibase have a feature where some messages sent to both the logger and the stdout / stderr via this UI Service - https://docs.liquibase.com/parameters/ui-service.html.

I couldn't seem to control it with the parameter in the docs so used a workaround suggested in these threads - liquibase/liquibase#3651 and liquibase/liquibase#2396. The change sets the ui service to use the LoggerUIService and set the level to FINE (maybe we could even set it to OFF). After these changes I can see liquibase is still logging but no longer flooding the logs with the duplicate changelog message.

This is another solution that doesn't seem ideal as using Scope.enter() mentions it is intended for tests - https://javadoc.io/doc/org.liquibase/liquibase-core/4.27.0/liquibase/Scope.html#enter(liquibase.listener.LiquibaseListener,java.util.Map) but I didn't notice any issues using it.

@dkayiwa
Copy link
Member

dkayiwa commented May 22, 2024

After these changes I can see liquibase is still logging but no longer flooding the logs with the duplicate changelog message.

I still have the Found 2 files with the path 'liquibase.xml' when i run the changes in this pull request. Or are you yet to commit more changes?

@k4pran
Copy link
Contributor Author

k4pran commented May 22, 2024

After these changes I can see liquibase is still logging but no longer flooding the logs with the duplicate changelog message.

I still have the Found 2 files with the path 'liquibase.xml' when i run the changes in this pull request. Or are you yet to commit more changes?

I found one place that I had missed and committed it, I will try to check more tomorrow in case I missed anywhere else

@dkayiwa
Copy link
Member

dkayiwa commented May 22, 2024

I will try to check more tomorrow in case I missed anywhere else

Awesome! 👍
Ping me when you are done such that i test again.

@k4pran
Copy link
Contributor Author

k4pran commented May 23, 2024

I will try to check more tomorrow in case I missed anywhere else

Awesome! 👍 Ping me when you are done such that i test again.

Hopefully there are not more logs now from my commit last night if you want to try again. I tested different combinations of initializing the database, but if you see it come up again please let me know how you set it up and maybe I can reproduce it, seems there are a lot of paths the lead to that log being triggered.

@dkayiwa
Copy link
Member

dkayiwa commented May 23, 2024

@k4pran this is the log that i get when running your current changes: https://pastebin.com/kSzkPEc1
Do you have any modules that are running?

@k4pran
Copy link
Contributor Author

k4pran commented May 24, 2024

@k4pran this is the log that i get when running your current changes: https://pastebin.com/kSzkPEc1 Do you have any modules that are running?

No I don’t have any modules running, will try it with other modules, are there many modules that call liquibase library directly or do they generally just uss core for that?

What way did you set it up with additional modules? When I used the openmrs-sdk and deployed the latest platform I ran into checksum issues, it mentions "Importing an initial database from classpath://openmrs-platform.sql...", maybe I need to update something here too?

EDIT: I was able to reproduce it with some modules and fix it. Maybe it would be useful for me to list the different ways I have initialized the database in case there are any gaps and you can advise if there are other ways I should try

  • Clean database on my snapshot version (running only core module)
  • Clean database on my snapshot version (running with three platform modules that I copy-pasted manually)
  • Already initialized database (2.2.x, 2.4.x, 2.6.x schema / core changelogs) on my snapshot version (running only core module)
  • Already initialized database (2.4.x, 2.6.x schema / core changelogs) on the current openmrs master version, then started up on my snapshot version
  • Starting up a platform with openmrs-sdk on the latest snapshot (failed due to checksums)

When running I have been running just as the jetty server from intellij. I pushed that small fix to handle with modules. I am starting work now but please let me know any other scenarios I should try and will try over the weekend. 🙂

@dkayiwa
Copy link
Member

dkayiwa commented May 24, 2024

@k4pran the fix worked! 😊

There is a new error showing up which you can reproduce by downloading this module https://addons.openmrs.org/show/org.openmrs.module.initializer and dropping it into the modules folder before restarting the app.

@k4pran
Copy link
Contributor Author

k4pran commented May 25, 2024

@k4pran the fix worked! 😊

There is a new error showing up which you can reproduce by downloading this module https://addons.openmrs.org/show/org.openmrs.module.initializer and dropping it into the modules folder before restarting the app.

I am having some difficulty reproducing an error for this module. I initialized the database on 2.6.x xmls, I started the jetty server which created this directory structure

Directory: C:\Users\ryanm\AppData\Roaming\OpenMRS


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        25/05/2024     09:34                configuration
-a----        25/05/2024     09:34              0 openmrs.log

Logs at this point - https://pastebin.com/5nx0NrNm

Then I created a modules folder and dropped the initializer omod in from the link and started the jetty server again. I went through the startup wizard with "simple -> no demo data".

My directory structure ended up

│   initializer.log
│   openmrs-runtime.properties
│   openmrs.1.log
│   openmrs.log
│
├───.openmrs-lib-cache
│   └───initializer
│       │   initializer.jar
│       │   liquibase.xml
│       │   moduleApplicationContext.xml
│       │   webModuleApplicationContext.xml
│       │
│       ├───lib
│       │       ascii-table-1.1.0.jar
│       │       commons-beanutils-1.7.0.jar
│       │       commons-codec-1.9.jar
│       │       commons-collections4-4.2.jar
│       │       commons-lang3-3.1.jar
│       │       commons-text-1.3.jar
│       │       initializer-api-2.2-2.6.0.jar
│       │       initializer-api-2.3-2.6.0.jar
│       │       initializer-api-2.4-2.6.0.jar
│       │       initializer-api-2.6.0.jar
│       │       initializer-api-bahmni-2.6.0.jar
│       │       opencsv-4.5.jar
│       │
│       └───org
│           └───openmrs
│               └───module
│                   └───initializer
│                       │   InitializerConfig.class
│                       │   InitializerMessageSource.class
│                       │
│                       ├───api
│                       │   │   BaseAttributeLineProcessor.class
│                       │   │   BaseLineProcessor.class
│                       │   │   CsvParser.class
│                       │   │   HibernateInitializerDAO.class
│                       │   │   OrderedFile.class
│                       │   │
│                       │   ├───attributes
│                       │   │   └───types
│                       │   │           AttributeTypeCsvLineHandlerImpl.class
│                       │   │           AttributeTypesCsvParser.class
│                       │   │           AttributeTypesLoader.class
│                       │   │           AttributeTypesProxyServiceImpl.class
│                       │   │           BaseAttributeTypeLineProcessor.class
│                       │   │
│                       │   ├───c
│                       │   │       ConceptAttributeLineProcessor.class
│                       │   │       ConceptClassesCsvParser.class
│                       │   │       ConceptClassesLoader.class
│                       │   │       ConceptClassLineProcessor.class
│                       │   │       ConceptComplexLineProcessor.class
│                       │   │       ConceptLineProcessor.class
│                       │   │       ConceptNumericLineProcessor.class
│                       │   │       ConceptsCsvParser.class
│                       │   │       ConceptSetLineProcessor.class
│                       │   │       ConceptSetsCsvParser.class
│                       │   │       ConceptSetsLoader.class
│                       │   │       ConceptsLoader.class
│                       │   │       ConceptSourceLineProcessor.class
│                       │   │       ConceptSourcesCsvParser.class
│                       │   │       ConceptSourcesLoader.class
│                       │   │       MappingsConceptLineProcessor.class
│                       │   │       NestedConceptLineProcessor.class
│                       │   │
│                       │   ├───display
│                       │   │       DisplayLineProcessor.class
│                       │   │       DisplaysCsvParser.class
│                       │   │       DisplaysPreLoader.class
│                       │   │
│                       │   ├───drugs
│                       │   │       DrugLineProcessor.class
│                       │   │       DrugsCsvParser.class
│                       │   │       DrugsLoader.class
│                       │   │       MappingsDrugLineProcessor.class
│                       │   │
│                       │   ├───er
│                       │   │       EncounterRoleLineProcessor.class
│                       │   │       EncounterRolesCsvParser.class
│                       │   │       EncounterRolesLoader.class
│                       │   │
│                       │   ├───et
│                       │   │       EncounterTypeLineProcessor.class
│                       │   │       EncounterTypesCsvParser.class
│                       │   │       EncounterTypesLoader.class
│                       │   │
│                       │   ├───freq
│                       │   │       OrderFrequenciesCsvParser.class
│                       │   │       OrderFrequenciesLoader.class
│                       │   │       OrderFrequencyLineProcessor.class
│                       │   │
│                       │   ├───gp
│                       │   │       GlobalPropertiesLoader.class
│                       │   │
│                       │   ├───loaders
│                       │   │       AmpathFormsLoader.class
│                       │   │       AmpathFormsTranslationsLoader.class
│                       │   │       BaseCsvLoader.class
│                       │   │       BaseFileLoader.class
│                       │   │       BaseInputStreamLoader.class
│                       │   │       BaseLoader.class
│                       │   │       CsvLoader.class
│                       │   │       JsonKeyValuesLoader.class
│                       │   │       LiquibaseLoader.class
│                       │   │       Loader.class
│                       │   │
│                       │   ├───loc
│                       │   │       LocationAttributeLineProcessor.class
│                       │   │       LocationLineProcessor.class
│                       │   │       LocationsCsvParser.class
│                       │   │       LocationsLoader.class
│                       │   │       LocationTagLineProcessor.class
│                       │   │       LocationTagMapsCsvParser.class
│                       │   │       LocationTagMapsLineProcessor.class
│                       │   │       LocationTagMapsLoader.class
│                       │   │       LocationTagsCsvParser.class
│                       │   │       LocationTagsLoader.class
│                       │   │
│                       │   ├───ot
│                       │   │       OrderTypeLineProcessor.class
│                       │   │       OrderTypesCsvParser.class
│                       │   │       OrderTypesLoader.class
│                       │   │
│                       │   ├───pat
│                       │   │       PersonAttributeTypeLineProcessor$Helper.class
│                       │   │       PersonAttributeTypeLineProcessor.class
│                       │   │       PersonAttributeTypesCsvParser.class
│                       │   │       PersonAttributeTypesLoader.class
│                       │   │
│                       │   ├───pit
│                       │   │       PatientIdentifierTypeLineProcessor.class
│                       │   │       PatientIdentifierTypesCsvParser.class
│                       │   │       PatientIdentifierTypesLoader.class
│                       │   │
│                       │   ├───privileges
│                       │   │       PrivilegeLineProcessor.class
│                       │   │       PrivilegesCsvParser.class
│                       │   │       PrivilegesLoader.class
│                       │   │
│                       │   ├───programs
│                       │   │   │   ProgramLineProcessor.class
│                       │   │   │   ProgramsCsvParser.class
│                       │   │   │   ProgramsLoader.class
│                       │   │   │
│                       │   │   └───workflows
│                       │   │       │   ProgramWorkflowLineProcessor.class
│                       │   │       │   ProgramWorkflowsCsvParser.class
│                       │   │       │   ProgramWorkflowsLoader.class
│                       │   │       │
│                       │   │       └───states
│                       │   │               ProgramWorkflowStateLineProcessor.class
│                       │   │               ProgramWorkflowStatesCsvParser.class
│                       │   │               ProgramWorkflowStatesLoader.class
│                       │   │
│                       │   ├───relationship
│                       │   │   └───types
│                       │   │           RelationshipTypeLineProcessor.class
│                       │   │           RelationshipTypesCsvParser.class
│                       │   │           RelationshipTypesLoader.class
│                       │   │
│                       │   ├───roles
│                       │   │       RoleLineProcessor.class
│                       │   │       RolesCsvParser.class
│                       │   │       RolesLoader.class
│                       │   │
│                       │   ├───utils
│                       │   │       ConceptClassListParser.class
│                       │   │       ConceptListParser.class
│                       │   │       ListParser.class
│                       │   │       LocationTagListParser.class
│                       │   │       PrivilegeListParser.class
│                       │   │       RoleListParser.class
│                       │   │
│                       │   └───visittypes
│                       │           VisitTypeLineProcessor.class
│                       │           VisitTypesCsvParser.class
│                       │           VisitTypesLoader.class
│                       │
│                       └───web
│                           └───controller
│                                   InitializerController.class
│
├───configuration
├───lucene
│   └───indexes
│       ├───org.openmrs.ConceptComplex
│       │       segments_1
│       │
│       ├───org.openmrs.ConceptName
│       │       segments_1
│       │
│       ├───org.openmrs.ConceptNumeric
│       │       segments_1
│       │
│       ├───org.openmrs.Drug
│       │       segments_1
│       │
│       ├───org.openmrs.PatientIdentifier
│       │       segments_1
│       │
│       ├───org.openmrs.PersonAttribute
│       │       segments_1
│       │
│       └───org.openmrs.PersonName
│               segments_1
│
└───modules
        initializer-2.6.0.omod

With logs https://pastebin.com/zGzJXY8a

Let me know if I am doing it different than you somehow and if you get time could you also share your logs please? I also tried it with omods fhir2-1.9.0, owa-1.14.0 and webservices.rest-2.38.0

@dkayiwa
Copy link
Member

dkayiwa commented May 26, 2024

This is my full log: https://pastebin.com/HLsmztHD

@k4pran
Copy link
Contributor Author

k4pran commented May 27, 2024

This is my full log: https://pastebin.com/HLsmztHD

I'm not sure that this error is related to my changes, on windows it seems to pass but windows is stringifying the null avoiding the NPE, unix file system is maybe handling it differently.

ERROR - Slf4JLogger.log(39) |2024-05-27T01:05:29,953| ChangeSet liquibase.xml::delete-concepts-checksums-20211025::iniz encountered an exception.
java.lang.NullPointerException: null
	at java.base/java.util.Objects.requireNonNull(Objects.java:209) ~[?:?]
	at java.base/sun.nio.fs.UnixFileSystem.getPath(UnixFileSystem.java:263) ~[?:?]
	at java.base/java.nio.file.Path.of(Path.java:147) ~[?:?]
	at java.base/java.nio.file.Paths.get(Paths.java:69) ~[?:?]
	at org.openmrs.module.initializer.api.ConfigDirUtil.<init>(ConfigDirUtil.java:72) ~[initializer-api-2.6.0.jar:?]
	at org.openmrs.module.initializer.api.ConfigDirUtil.<init>(ConfigDirUtil.java:81) ~[initializer-api-2.6.0.jar:?]
	at org.openmrs.module.initializer.liquibase.DeleteDomainChecksumsChangeset.execute(DeleteDomainChecksumsChangeset.java:52) ~

On windows I get a success

INFO - Slf4JLogger.log(43) |2024-05-27T08:31:57,345| Reading from openmrs.liquibasechangelog
Running Changeset: liquibase.xml::delete-concepts-checksums-20211025::iniz
INFO - Slf4JLogger.log(43) |2024-05-27T08:40:39,056| Finished deleting all checksums files for the concepts domain.
INFO - Slf4JLogger.log(43) |2024-05-27T08:40:39,076| ChangeSet liquibase.xml::delete-concepts-checksums-20211025::iniz ran successfully in 521687ms

I think the issue might be that null is being hardcoded here - here as this null is what is being used to create the path.

I will try to set up a linux environment to try it out.

@dkayiwa
Copy link
Member

dkayiwa commented May 27, 2024

I will try to set up a linux environment to try it out.

Instead of taking you through all the linux environment setup, how about just fixing initializer module? And then i test it for you on the linux environment. 😊

@k4pran
Copy link
Contributor Author

k4pran commented May 28, 2024

I will try to set up a linux environment to try it out.

Instead of taking you through all the linux environment setup, how about just fixing initializer module? And then i test it for you on the linux environment. 😊

I made the change here and did a quick regression test on windows, please give it a go on linux and let me know - https://github.com/mekomsolutions/openmrs-module-initializer/pull/267/files

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants