| ## Migration from H2 Caches | |
| This module provides a REST API to help converting existing cache from H2 to | |
| chronicle-map, which requires the `Administrate Caches` or `Administrate Server` | |
| capabilities to be executed. | |
| The migration must be executed _before_ switching to use chronicle-map, while | |
| Gerrit cache is still backed by H2. | |
| The migration can be run online without any disruption of the Gerrit server. | |
| However note that since the migration perform many, sequential reads from the H2 | |
| database, it will participate to the overall database load, so you should test | |
| accordingly. | |
| The migration would do the following: | |
| 1. scan all existing cache key-value pairs | |
| 2. calculate the parameters for the new cache, if not already defined in gerrit.config. | |
| 3. create the new cache | |
| 4. read all existing key-value pairs and insert them into the new cache-chroniclemap files | |
| > **NOTE**: The existing cache parameters are kept in `gerrit.config` only when they are all | |
| > defined (avgKeySize, avgValueSize, maxEntries and maxBloatFactor), otherwise the | |
| > migration process will recalculate them and create the new cache based on the new | |
| > values. | |
| The following caches will be migrated (if they exist and contain any data): | |
| * accounts | |
| * change_kind | |
| * change_notes | |
| * conflicts | |
| * diff | |
| * diff_intraline | |
| * diff_summary | |
| * git_tags | |
| * mergeability | |
| * oauth_token | |
| * persisted_projects | |
| * pure_revert | |
| * web_sessions | |
| The migration should be performed as follows: | |
| * Copy `cache-chroniclemap.jar` file in the `plugins/` directory. | |
| * Wait for the pluginLoader to load the new plugin. You will see an entry in | |
| the `error_log`: | |
| ``` | |
| INFO com.google.gerrit.server.plugins.PluginLoader : Loaded plugin cache-chroniclemap | |
| ``` | |
| * You can now run the migration | |
| ```bash | |
| curl -v -XPUT -u <admin> '<gerrit>/a/plugins/cache-chroniclemap/migrate?[size-multiplier=FACTOR]&[bax-bloat-factor=MULTIPLIER]' | |
| ``` | |
| This might require some time, depending on the size of the H2 caches and it will | |
| terminate with the output of the configuration that should be places in | |
| `etc/gerrit.config`in order to leverage the newly created caches correctly. | |
| Output example: | |
| ``` | |
| [cache "diff"] | |
| maxEntries = 216 | |
| avgKeySize = 188 | |
| avgValueSize = 796 | |
| maxBloatFactor = 1 | |
| [cache "persisted_projects"] | |
| maxEntries = 3 | |
| avgKeySize = 80 | |
| avgValueSize = 4087 | |
| maxBloatFactor = 1 | |
| [cache "diff_summary"] | |
| maxEntries = 216 | |
| avgKeySize = 192 | |
| avgValueSize = 254 | |
| maxBloatFactor = 1 | |
| [cache "accounts"] | |
| maxEntries = 2 | |
| avgKeySize = 52 | |
| avgValueSize = 194 | |
| maxBloatFactor = 1 | |
| [cache "mergeability"] | |
| maxEntries = 2444 | |
| avgKeySize = 150 | |
| avgValueSize = 20 | |
| maxBloatFactor = 1 | |
| [cache "web_sessions"] | |
| maxEntries = 94852 | |
| avgKeySize = 68 | |
| avgValueSize = 382 | |
| maxBloatFactor = 1 | |
| ``` | |
| Optionally the REST endpoint can receive the following additional arguments: | |
| * max-bloat-factor=FACTOR | |
| maximum number of times chronicle-map cache is allowed to grow in size. | |
| *default:3* | |
| * size-multiplier=MULTIPLIER | |
| Multiplicative factor for the number of entries allowed in chronicle-map. | |
| *default:3* | |
| ### Reloading plugins with persistent caches backed by chroniclemap | |
| When chroniclemap store is initiated for a cache it locks exclusively the | |
| underlying file and keeps it until store is closed. Store is closed when Gerrit | |
| is stopped (for core caches) or when plugin is unloaded through either REST or | |
| SSH command. The later is problematic from the plugin `reload` command | |
| perspective as by default it unloads old version of plugin once new version is | |
| successfully loaded. Considering that old version holds the lock until it gets | |
| unloaded new version load will not succeed. As a result the following (or | |
| similar) error is visible in the log: | |
| ``` | |
| [2022-08-31T17:37:56.481+02:00] [SSH gerrit plugin reload test-cache-plugin (admin)] WARN com.google.gerrit.server.plugins.PluginLoader : Cannot reload plugin test-cache-plugin | |
| com.google.inject.CreationException: Unable to create injector, see the following errors: | |
| 1) [Guice/ErrorInCustomProvider]: ChronicleHashRecoveryFailedException: ChronicleFileLockException: Unable to acquire an exclusive file lock for gerrit/cache/test-cache-plugin.test_cache_0.dat. Make sure no other process is using the map. | |
| at CacheModule.bindCache(CacheModule.java:188) | |
| \_ installed by: Module -> TestCache$1 | |
| while locating Cache<String, String> annotated with @Named(value="test_cache") | |
| Learn more: | |
| https://github.com/google/guice/wiki/ERROR_IN_CUSTOM_PROVIDER | |
| Caused by: ChronicleHashRecoveryFailedException: ChronicleFileLockException: Unable to acquire an exclusive file lock for gerrit/cache/test-cache-plugin.test_cache_0.dat. Make sure no other process is using the map. | |
| at ChronicleMapBuilder.openWithExistingFile(ChronicleMapBuilder.java:1937) | |
| at ChronicleMapBuilder.createWithFile(ChronicleMapBuilder.java:1706) | |
| at ChronicleMapBuilder.recoverPersistedTo(ChronicleMapBuilder.java:1622) | |
| ... | |
| ``` | |
| The following steps can be used in order to perform reload operation: | |
| 1. perform the plugin `reload` in two steps by calling `remove` first and | |
| following it with `add` command - the easiest way that doesn't require any | |
| code modification | |
| 2. add `Gerrit-ReloadMode: restart` to plugin's manifest so the when the plugin | |
| `reload` command is called Gerrit unloads the old version prior loading the | |
| new one - requires plugin's sources modification and build which might be | |
| not an option in certain cases |