[#1068] Apply what a running backend takes of a configuration change, and ask for a restart for what it does not - #1069
Conversation
48a7d9e to
4bc401f
Compare
|
Rebased onto the restacked #1066 ( Re-run green on the rebased head: |
4bc401f to
4ea1d18
Compare
|
Restacked on the new head of #1066 ( |
4ea1d18 to
d2da8dd
Compare
|
Restacked on the new head of #1066 ( No change of its own to re-test either; |
maximthomas
left a comment
There was a problem hiding this comment.
praise: The change goes where #1068 says the gap is and reports through the channel that reaches the log.
JEStorage.applyToEnvironment(JEStorage.java:1441-1465) is the firstEnvironment.setMutableConfigcall insrc/main, and the six mapped properties plus the durability every way reach the running environment (aChangeOfWhatJETakesWhileItRunsReachesTheEnvironment,aChangeOfTheDurabilityReachesTheEnvironmentEveryWay).- Every differing immutable parameter goes into the change result with
NOTE_CONFIG_DB_PROPERTY_REQUIRES_RESTART(631) andadminActionRequired, not only into the XML marking, and the both-flags durability is now refused byisConfigurationChangeAcceptable(checkEnvironmentConfiguration,JEStorage.java:1337) instead of failing the next open. - Green on CI at this head:
JEStorageTest17/17 andPDBStorageTest22/22 on build-maven (ubuntu-latest, 11).
issue (blocking): Removing a mutable je-property leaves the running environment on the old value, and the change result reports it applied.
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java:1449, :1465; opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/JEBackendConfiguration.xml:733
applyToEnvironment builds next from cfg and hands it to env.setMutableConfig(next), and the loop skips every mutable parameter. In JE 18.3.12, EnvironmentImpl.doSetMutableConfig clones the running config and EnvironmentMutableConfig.copyMutablePropsTo copies only the names next sets explicitly (its Properties has no defaults). So after dsconfig set-backend-prop --remove je-property:je.cleaner.minAge=5, next no longer sets je.cleaner.minAge, and the environment runs with 5 until the next open. The change result carries no message and no admin action. That is the "reported as applied, runs on unchanged" outcome #1068 removes, on a road the description claims ("a mutable je-property is applied") and the new je-property synopsis documents ("... and the change result says so"). Replacing a value is applied, and removing an immutable one is reported: getConfigParam resolves an unset name to JE's default, so the diff sees it. Only the mutable removal is dropped. Mapped properties and durability are always set by toEnvironmentConfig and are not affected.
for (ConfigParam param : new TreeMap<>(EnvironmentParams.SUPPORTED_PARAMS).values())
{
// Replication parameters are not set through an environment configuration; a multi-value
// parameter is not read as one value. Neither is set by this storage.
if (param.isForReplication() || param.isMultiValueParam())
{
continue;
}
final String runningValue = running.getConfigParam(param.getName());
final String nextValue = next.getConfigParam(param.getName());
if (Objects.equals(runningValue, nextValue))
{
continue;
}
if (param.isMutable())
{
// setMutableConfig copies only what the configuration handed to it sets: a parameter the
// configuration no longer sets goes back to JE's default rather than keeping its value.
next.setConfigParam(param.getName(), nextValue);
}
else
{
ccr.setAdminActionRequired(true);
ccr.addMessage(NOTE_CONFIG_DB_PROPERTY_REQUIRES_RESTART.get(
ConfigurableEnvironment.configuredNameOf(param.getName()), cfg.getBackendId(), runningValue, nextValue));
}
}Pin (JEStorageTest, red at this head): apply a cfg whose getJEProperty() is new TreeSet<>(Arrays.asList(CLEANER_MIN_AGE + "=5")), assert env.getMutableConfig().getConfigParam(CLEANER_MIN_AGE) is "5", then apply createBackendCfg() and assert getMessages() is empty and the parameter is back to the value read before the first change.
issue (non-blocking): After an open the quota refused, every modify of the backend entry is refused. This is #1066's blocking item, carried here by the stacked commit.
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java:1289, opendj-server-legacy/src/main/java/org/opends/server/backends/pdb/PDBStorage.java:1557
(newSize <= reservedCacheSize || quota.isMemoryAvailable(newSize - reservedCacheSize)) with reservedCacheSize == 0 refuses a modify that leaves the cache alone. That includes the db-checkpointer-wakeup-interval change this PR reports on, and TaskUtils.disableBackend for an online import, rebuild or restore. It comes from bd9d9d1 (#1066) and d2da8dd leaves it alone; the new checks only follow the &&. It is blocking on #1066, not twice. Rebasing onto #1066's fix clears it here.
issue (non-blocking): A modify that also moves db-directory returns before applyToEnvironment and the checkpoint arm run.
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java:1393, :1416; opendj-server-legacy/src/main/java/org/opends/server/backends/pdb/PDBStorage.java:1639, :1660
The permissions block is entered whenever the directory differs, and if (!ccr.getMessages().isEmpty()) return ccr; then returns because NOTE_CONFIG_DB_DIR_REQUIRES_RESTART is already in the result, not because of an error. A modify of db-directory plus db-txn-no-sync, a je-property, or the PDB interval therefore gets the directory note only: the durability is not applied, no 631 names the immutable change, and config = cfg is skipped, so every later modify before the restart takes the same return. The return predates this PR, but the new application and reporting sit below it. The directory note already asks for a restart, which limits the harm. StorageUtils.addErrorMessage sets the result code, so test that instead of whether the result has messages, in both storages (with import org.forgerock.opendj.ldap.ResultCode;):
if (ccr.getResultCode() != ResultCode.SUCCESS)
{
return ccr;
}suggestion (non-blocking): No case changes db-cache-percent while the storage is open, so nothing pins the MAX_MEMORY_PERCENT copy.
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java:1463; opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/JEStorageTest.java:492, :655
Every test cfg stubs getDBCachePercent() to 20, and the one cache case (aChangeWhileOpenLeavesTheCacheWhereTheOpenReservedIt) changes db-cache-size on a SMALL_CACHE environment. The copy is therefore a no-op in every case, and deleting :1463 survives the file. On a percent-sized backend (db-cache-size 0, the default), dropping the copy would let a percent change resize the live cache while the quota still holds the open's reservation.
@Test
public void aCachePercentChangedWhileOpenLeavesTheCacheWhereTheOpenReservedIt() throws Exception
{
final Environment env = environmentOf(storage);
final long cacheAtOpen = env.getMutableConfig().getCacheSize();
final JEBackendCfg cfg = createBackendCfg();
when(cfg.getDBCachePercent()).thenReturn(30);
assertThat(storage.applyConfigurationChange(cfg).adminActionRequired()).isTrue();
assertThat(env.getMutableConfig().getCacheSize()).isEqualTo(cacheAtOpen);
}Pin: red with :1463 deleted.
suggestion (non-blocking): No test pins that the PDB checkpoint arm compares against the interval the database runs with rather than against the previous configuration.
opendj-server-legacy/src/test/java/org/opends/server/backends/pdb/PDBStorageTest.java:716; opendj-server-legacy/src/main/java/org/opends/server/backends/pdb/PDBStorage.java:1660
Both interval cases make one change from the open state, where config's interval equals the database's. The mutant cfg.getDBCheckpointerWakeupInterval() != config.getDBCheckpointerWakeupInterval(), with the from-value read off config, gives the same text on the open case and stays silent on the closed one. Under it, a pending restart stops being reported after an unrelated change. Append to aCheckpointIntervalChangedWhileOpenAsksForARestart:
// a later change which leaves the interval where the first one put it still asks for the restart
assertThat(storage.applyConfigurationChange(cfg).adminActionRequired()).isTrue();
// one which puts it back to what the database runs asks for nothing
assertThat(storage.applyConfigurationChange(createBackendCfg()).getMessages()).isEmpty();Pin: both lines go red under the mutant.
…han what the configuration says by then, and ask for a restart when the cache size changes PDBStorage and JEStorage reserved their cache size from the memory quota by reading config in buildConfiguration and released it by reading config again in close(). applyConfigurationChange swapped config in between without touching the quota or the cache, and neither db-cache-size nor db-cache-percent was marked as needing a restart, so a cache grown from 64 MB to 128 MB while the backend ran released 128 against 64 taken at the next disable - the one an online import makes included - and the quota believed 64 MB free that the server did not have, for the life of the JVM; a shrink left the difference reserved by nobody. The running cache was the old size throughout. Both storages now keep two numbers of their own: the cache size of the configuration they opened with, and of it what the quota granted - a tryAcquire it refused, which an open at startup is not checked against, reserved nothing and used to be released all the same. close() gives back the granted size. isConfigurationChangeAcceptable admits the difference to what is held rather than to config, which a change admitted but not yet applied has already moved to the new size. applyConfigurationChange on an open storage whose cache size the change moves sets adminActionRequired and says so (NOTE_CONFIG_DB_CACHE_REQUIRES_RESTART): PersistIt cannot resize a buffer pool once the database is open, and JEStorage has never resized its environment. The two properties are marked component-restart in both configuration XMLs, as db-directory is. PDBStorageTest and JEStorageTest, six cases each: the grow and the shrink give back what was taken, the change asks for a restart and names both sizes, a change which leaves the cache alone asks for nothing, admission is against what is held, and a reservation the quota refused is not given back.
…che past the configured size whatever the storage holds After an open the quota refused, a storage holds nothing of the quota, and the admission of the last commit measured every change against that nothing: newSize <= reservedCacheSize failed for any cache, and isMemoryAvailable(newSize) asked for the very amount the quota had just refused. Every change listener of the backend entry is asked about every change, whatever property it moves, so a change of db-txn-no-sync, a disable, and the disable TaskUtils.disableBackend makes for an online import-ldif, rebuild-index or restore were all refused with UNWILLING_TO_PERFORM and no reason. The state needs no change of configuration to reach: the server does not check the backends it opens at startup against the quota. A size which does not grow past the one configured now asks the quota for nothing again, as it did before this PR; a growth is still measured against what the storage holds, so the case of a change admitted but not applied keeps its outcome. NOTE 630 no longer calls the size the backend was opened with reserved, which after a refused open it is not. PDBStorageTest and JEStorageTest, five more cases each and one extended, each killing a mutant which survived both classes: a change which leaves the cache alone is admitted after a refused reservation, a growth after it is measured against nothing held, a shrink is admitted with the quota exhausted, a cache sized by percent asks for a restart only when the percent moves, a storage which is not open asks for none, and a change back to the size the storage opened with asks for nothing.
…onfiguration change, and ask for a restart for what it does not Nine properties of the JE and PDB backends were neither applied to a running backend nor marked as requiring a restart. JEStorage.applyConfigurationChange handled the directory, its permissions and the disk thresholds and left the environment - configured once, at the open - as it was, while the XML kept db-cleaner-min-utilization, db-run-cleaner, db-evictor-core-threads, db-evictor-max-threads, db-evictor-keep-alive, db-num-cleaner-threads, db-txn-no-sync and db-txn-write-no-sync (JE) and db-checkpointer-wakeup-interval (PDB) as live properties, which they had been in the local-db backend OPENDJ-1719 replaced. A change of any of them was reported as applied while the backend ran on unchanged until it was next opened; so was a native property changed through je-property. JEStorage now builds the environment configuration the changed configuration describes and hands it to Environment.setMutableConfig, which takes of it what JE accepts while it runs: the properties above, the durability, and a mutable native property - all but the cache, which stays with the memory reserved for it until the restart OpenIdentityPlatform#1063 asks for. Every immutable JE parameter whose value differs from the running environment's is reported with the new NOTE 631, which names the property, the value the environment runs with and the one configured, and reaches the error log as a warning - where the change result of a property marked in the XML alone never did. An import's environment is left alone: it runs on a configuration of its own, and the backend opens again on the changed one once the import is over. The build of the environment configuration is split from the checks of the open (ConfigurableEnvironment.toEnvironmentConfig): no cache size probe against the memory quota, no level set on the JE loggers, so that a configuration change can be checked against it as well - and it is: isConfigurationChangeAcceptable and isConfigurationAcceptable refuse a durability which sets both flags (db-txn-write-no-sync is on by default, so setting db-txn-no-sync alone is one) and a native property JE does not know before the change is written. Nothing checked either before, and the backend failed to open on them at its next restart. A configuration which sets neither durability flag now sets COMMIT_SYNC explicitly: what JE falls back on, but set, since JE leaves the durability an environment has in place when a configuration hands it none. PDBStorage reports a change of db-checkpointer-wakeup-interval with the same note, holding the configured interval against the one the database opened with - PersistIt takes no configuration once one is set - and the property is marked component-restart in PDBBackendConfiguration.xml. The definition of je-property says which of its changes wait for a restart.
…s default, and apply the rest of a change which moves the directory A mutable je-property removed from the configuration stayed in the running environment: setMutableConfig copies only what the configuration handed to it sets, onto a clone of the running one. applyToEnvironment now hands JE's default explicitly for a mutable parameter the configuration no longer sets, and asks for a restart (631) where JE does not take its own default as a value - je.cleaner.readSize, whose 0 means "computed at the open", is refused below 128. The two checks after the directory permissions returned on any message, and the note of a moved db-directory is one: a change which moved the directory as well skipped the environment, the checkpoint interval, the cache note and config = cfg. Both storages now return on the result code StorageUtils.addErrorMessage sets. Tests: the removal applied, the removal JE cannot take while it runs, a moved directory with the rest of the change (JE and PDB), a percentage change which leaves the live cache alone, and the checkpoint interval held against the database across two changes.
d2da8dd to
871db80
Compare
|
Round head: Blocking: a removed mutable
So
Non-blocking: every modify refused after a refused open. This is fixed by #1066's round and carried here by the restack: Non-blocking: a moved
Suggestion: the percent pin. Taken as Suggestion: the checkpoint interval across two changes. Your two lines are appended to Runs. Reactor
Each mutant is red on exactly the cases listed and green elsewhere. |
Fixes #1068. Stacked on #1066 (
cba9d511b5, on master0e039c6473): the same lines of bothapplyConfigurationChange, both XMLs and the ordinal after its 630;JEStorageTestexists only from #999 on. Merge after #1066.What was wrong
Nine properties of the JE and PDB backends were neither applied to a running backend nor marked as requiring a restart, so
dsconfigreported a change of them applied and the backend ran on unchanged until it was next opened.JEStorage.applyConfigurationChangehandleddb-directory, its permissions and the disk thresholds and left the environment - configured once, at the open - as it was; nothing in the server calledEnvironment.setMutableConfig. The same went for a native property changed throughje-property, mutable or not (not in the issue's table, same cause).Two things the issue had wrong, found on the way:
requires-admin-actionin the XML reaches the reference documentation and property help alone -dsconfigprints nothing about it at set time, and a change result withoutadminActionRequiredcarries nothing to the error log either. So "asdb-log-file-maxalready does" was not true of the change result: the marking is all those properties had. The oldRootContainerreported every changed immutable parameter in the change result, which the server logs as a warning (WARN 647); that shape is restored for all of them.db-txn-write-no-syncis on by default, sodsconfig set-backend-prop --set db-txn-no-sync:trueon a JE backend yields a durability which sets both flags. Nothing checked that at change time: the change was admitted and written, and the backend failed to open onERR_CONFIG_JEB_DURABILITY_CONFLICTat its next restart. (aChangeWhichLeavesTheCacheSizeAloneAsksForNothingof [#1063] Give back what the open reserved rather than what the configuration says by then, and ask for a restart when the cache size changes #1066 used exactly that as its "unrelated change"; it now sets the write flag off as well.)What this does
JE -
applyToEnvironmentbuilds the environment configuration the changed configuration describes (ConfigurableEnvironment.toEnvironmentConfig) and hands it toEnvironment.setMutableConfig, which takes of it what JE accepts while it runs:db-cleaner-min-utilization,db-run-cleaner,db-evictor-core-threads,db-evictor-max-threads,db-evictor-keep-alive,db-num-cleaner-threads, the durability (db-txn-no-sync/db-txn-write-no-sync, every way - a configuration which sets neither now setsCOMMIT_SYNCexplicitly, since JE leaves the durability an environment has in place when handed none) and a mutableje-property- removed as well as set: the environment keeps the value it runs with of a parameter it is not handed, so a mutable parameter the configuration no longer sets is handed JE's default explicitly, and one whose default JE does not take as a value (je.cleaner.readSize, whose0stands for "computed at the open") asks for a restart with 631 instead. All but the cache:je.maxMemory/je.maxMemoryPercentare mutable too, but the cache stays with the memory reserved for it until the restart #1063 asks for, so the change hands the environment its current values back. Every immutable JE parameter whose value differs from the running environment's is reported with the newNOTE_CONFIG_DB_PROPERTY_REQUIRES_RESTART(631), naming the property asdsconfigknows it (or the JE property name forje-property), the value the environment runs with and the one configured. An import's environment is left alone - it runs on a configuration of its own and the backend opens again on the changed one once the import is over; held to the import's configuration, every property the import sets differently would ask for a restart.A change which moves
db-directoryas well is still applied and reported: both storages returned on any message after the permission checks, and the note of the moved directory is one - they now return on the result code an error sets.toEnvironmentConfigis the build alone - no cache size probe against the memory quota (#1067), no level set on the JE loggers (that moves toparseConfigEntry, the open's road) - so that a change can be checked against it:isConfigurationChangeAcceptableandisConfigurationAcceptablenow refuse a conflicting durability and a native property JE does not know before the change is written.PDB -
db-checkpointer-wakeup-intervalis set on the PersistIt configuration at the open alone andPersistit.setConfigurationrefuses once one is set, so a change of it reports 631 against the interval the database opened with (db.getConfiguration().getCheckpointInterval(), no new field), and the property is markedcomponent-restartinPDBBackendConfiguration.xml.je-property's definition says which of its changes wait for a restart.Left as they are:
db-logging-levelanddb-logging-file-handler-onkeep theircomponent-restartmarking. The JUL level is set by the open alone, as before; the file handler's level (je.env.fileLoggingLevel) is mutable in JE and so follows a change from now on - the marking is conservative about it, not wrong.Tests
JEStorageTest: the six mapped properties reachenv.getMutableConfig(); the durability follows the change every way; a mutableje-propertyis applied and an immutable one asks for a restart with 631; a removed mutable one goes back to JE's default, or asks for a restart where JE refuses its default; a change which moves the directory is still applied; a change ofdb-cache-percentleaves the live cache alone;db-log-file-maxasks for a restart with 631; a change while open leaves the cache where the open reserved it (the #1063 interplay); a change during an import leaves the import's environment alone; a change while closed touches nothing; a durability which sets both flags and an unknown native property are refused by both acceptability checks.PDBStorageTest: a changed interval asks for a restart with 631 and the database keeps its own, held against the database across two changes; a change which moves the directory still reports the interval; a change while closed asks for nothing.Verified locally: the issue reproduced on master, on the #999 head and on the #1066 head with a direct TestNG repro of every row (JE 7/7 red, PDB 1/1 red,
db-txn-no-syncon PDB green as a control); the new tests are red without the fix (5 JE + 1 PDB) and green with it; mutants (nosetMutableConfig, cache not pinned, immutables unreported, import's environment held to the configuration, PDB silent) each red on their own case; regression set of 18 classes / 240 tests (FailedBackendOpenTest, PDB/JE TestCase + Encrypted, ReplayedConfigChangeTest, OnDiskMergeImporterTest, the pluggable tree tests, ImportLDIF/RebuildIndex/VerifyIndex, BackendConfigManagerTestCase) green.