ServiceBuilder and Upgrade Processes

Introduction

Today I ran into someone having issues with ServiceBuilder and the creation of UpgradeProcess implementations. The doco is a little bit confusing, so I thought I'd do a quick blog post sharing how the pieces fit...

Normal UpgradeProcess Implementations

As a reminder, you register UpgradeProcess implementations to support upgrading from, say, 1.0.0 to 2.0.0, when there are things that you need to code to ensure when the upgrade is complete that the system is ready to use your module. Say, for example, that you're storing XML in a column in the DB and in 2.0.0 you've changed the DTD; for those folks that already have 1.0.0 deployed, your UpgradeProcess implementation would be responsible for processing each existing record in the database to change the contents over to the 2.0.0 version of the DTD. For non-ServiceBuilder modules, it is up to you to write the initial UpgradeProcess code for the 0.0.0 -> 1.0.0 version.

Through the lifespan of your plugin, you continue to add in UpgradeProcess implementations to handle the automatic update for dot releases and major releases. The best part is that you don't have to care what version everyone is using, Liferay will apply the right upgrade processes to take the users from what version they're currently at all the way through to the latest version.

This is all good, of course, but ServiceBuilder, well it behaves a little differently.

ServiceBuilder service.xml Development

As you go through development and you change the entities in service.xml and rebuild services, ServiceBuilder will update the SQL files used to create the tables, indices, etc. When you deploy the service the first time, ServiceBuilder will happily identify the initial deployment and will use the SQL files to create the entities.

This is where things can go sideways... If I deploy version 1.0.0 of the service and version 2.0.0 comes out, the service developer needs to implement an UpgradeProcess that makes the necessary changes to the tables to get things ready for the current version of the service. If you did not deploy version 1.0.0 but are starting out on 2.0.0, you don't want to have to execute all of the upgrade processes individually, you want ServiceBuilder to do what it has always done and just use the SQL files to create the version 2.0.0 of the entities.

So how do you support both of these scenarios correctly?

By using the Liferay-Require-SchemaVersion header¹ in your bnd.bnd file, that's how.

Supporting Both ServiceBuilder Upgrade Scenarios

The Liferay-Require-SchemaVersion header defines the current DB schema version number for your service modules. This version number should be incremented as you change your service.xml in preparation for a release.

There's code in the ServiceBuilder deployment which injects a hidden UpgradeProcess implementation that is defined to cover the "0.0.0" version (the version which represents the "new deployment") to the Liferay-Require-SchemaVersion version number.  So your first release will have the header set to 1.0.0, next release might be 2.0.0, etc.

So in our previous example with the 2.0.0 service release, when you deploy the service Liferay will match the "0.0.0" to "2.0.0" hidden upgrade process implementation provided by ServiceBuilder and will invoke it to get the 2.0.0 version of the tables, indices, etc. created for you using the SQL files.

The service developer must also code and register the manual UpgradeProcess instances that support the incremental upgrade. So for the example, there would need to be a 1.0.0 -> 2.0.0 UpgradeProcess implementation so when I deploy 2.0.0 to replace my 1.0.0 deployment, the UpgradeProcess will be used to modify my DB schema to get it up to version 2.0.0.

Conclusion

As long as you properly manage both the Liferay-Require-SchemaVersion header in the bnd.bnd file and provide your corresponding UpgradeProcess implementations, you will be able to easily handle the first time deployment as well as the upgrade deployments.

An important side effect to note here - you must manage your Liferay-Require-SchemaVersion correctly.  If you set it initially to 1.0.0 and forget to update it on future releases, your users will have all kinds of issues.  For initial deployments, the SQL scripts would create the entities using the latest SQL files and then try to apply UpgradeProcess implementations to get to new versions trying to make modifications they really don't have to worry about. For upgrade deployments, Liferay may not process upgrades because it believes the schema is already at the appropriate version.

¹ If the Liferay-Require-SchemaVersion header is missing, the value for the Bundle-Version will be used instead.

Blogs
Hi David,

I tried it and upgrade process did update the database schema as expected. Writing upgrade process seems good considering modular approach and production environment. But it seems at least to me an overhead considering continuous changes to schema during development phase.

Is there a way to update the database schema as per service.xml during development phase to reduce this upgrade process overhead?

Regards,
Harish
The upgrade process and the Liferay-Require-SchemaVersion thing is definitely not meant for development, it's meant for releases.

Personally I tend to stick to HSQL for as much of the service development that I can, it is just so easy to copy the DB script and restore later on, effectively dropping the changes made during previous service deployment.

This blog post needs to be included in the Official documentation! A straight-forward explanation of how the Bundle Version, Liferay-Require-SchemaVersion, and ServiceBuilder build.number relate (or don't) to each other wouldn't hurt either.