Search in sources :

Example 16 with NodeEnvironment

use of org.opensearch.env.NodeEnvironment in project OpenSearch by opensearch-project.

the class PersistedClusterStateServiceTests method testFailsIfIndexMetadataIsDuplicated.

public void testFailsIfIndexMetadataIsDuplicated() throws IOException {
    // if someone attempted surgery on the metadata index by hand, e.g. deleting broken segments, then maybe some index metadata
    // is duplicated
    final Path[] dataPaths1 = createDataPaths();
    final Path[] dataPaths2 = createDataPaths();
    final Path[] combinedPaths = Stream.concat(Arrays.stream(dataPaths1), Arrays.stream(dataPaths2)).toArray(Path[]::new);
    try (NodeEnvironment nodeEnvironment = newNodeEnvironment(combinedPaths)) {
        final String indexUUID = UUIDs.randomBase64UUID(random());
        final String indexName = randomAlphaOfLength(10);
        try (Writer writer = newPersistedClusterStateService(nodeEnvironment).createWriter()) {
            final ClusterState clusterState = loadPersistedClusterState(newPersistedClusterStateService(nodeEnvironment));
            writeState(writer, 0L, ClusterState.builder(clusterState).metadata(Metadata.builder(clusterState.metadata()).version(1L).coordinationMetadata(CoordinationMetadata.builder(clusterState.coordinationMetadata()).term(1L).build()).put(IndexMetadata.builder(indexName).version(1L).settings(Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1).put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0).put(IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey(), Version.CURRENT).put(IndexMetadata.SETTING_INDEX_UUID, indexUUID)))).incrementVersion().build(), clusterState);
        }
        final Path brokenPath = randomFrom(nodeEnvironment.nodeDataPaths());
        final Path dupPath = randomValueOtherThan(brokenPath, () -> randomFrom(nodeEnvironment.nodeDataPaths()));
        try (Directory directory = new NIOFSDirectory(brokenPath.resolve(PersistedClusterStateService.METADATA_DIRECTORY_NAME));
            Directory dupDirectory = new NIOFSDirectory(dupPath.resolve(PersistedClusterStateService.METADATA_DIRECTORY_NAME))) {
            try (IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig())) {
                // do not duplicate global metadata
                indexWriter.deleteDocuments(new Term("type", "global"));
                indexWriter.addIndexes(dupDirectory);
                indexWriter.commit();
            }
        }
        final String message = expectThrows(IllegalStateException.class, () -> newPersistedClusterStateService(nodeEnvironment).loadBestOnDiskState()).getMessage();
        assertThat(message, allOf(containsString("duplicate metadata found"), containsString(brokenPath.toString()), containsString(indexName), containsString(indexUUID)));
    }
}
Also used : Path(java.nio.file.Path) ClusterState(org.opensearch.cluster.ClusterState) NIOFSDirectory(org.apache.lucene.store.NIOFSDirectory) NodeEnvironment(org.opensearch.env.NodeEnvironment) IndexWriter(org.apache.lucene.index.IndexWriter) Matchers.containsString(org.hamcrest.Matchers.containsString) Term(org.apache.lucene.index.Term) IndexWriter(org.apache.lucene.index.IndexWriter) Writer(org.opensearch.gateway.PersistedClusterStateService.Writer) Directory(org.apache.lucene.store.Directory) FilterDirectory(org.apache.lucene.store.FilterDirectory) NIOFSDirectory(org.apache.lucene.store.NIOFSDirectory) IndexWriterConfig(org.apache.lucene.index.IndexWriterConfig)

Example 17 with NodeEnvironment

use of org.opensearch.env.NodeEnvironment in project OpenSearch by opensearch-project.

the class PersistedClusterStateServiceTests method testPersistsAndReloadsIndexMetadataIffVersionOrTermChanges.

public void testPersistsAndReloadsIndexMetadataIffVersionOrTermChanges() throws IOException {
    try (NodeEnvironment nodeEnvironment = newNodeEnvironment(createDataPaths())) {
        final PersistedClusterStateService persistedClusterStateService = newPersistedClusterStateService(nodeEnvironment);
        final long globalVersion = randomLongBetween(1L, Long.MAX_VALUE);
        final String indexUUID = UUIDs.randomBase64UUID(random());
        final long indexMetadataVersion = randomLongBetween(1L, Long.MAX_VALUE);
        final long oldTerm = randomLongBetween(1L, Long.MAX_VALUE - 1);
        final long newTerm = randomLongBetween(oldTerm + 1, Long.MAX_VALUE);
        try (Writer writer = persistedClusterStateService.createWriter()) {
            ClusterState clusterState = loadPersistedClusterState(persistedClusterStateService);
            writeState(writer, 0L, ClusterState.builder(clusterState).metadata(Metadata.builder(clusterState.metadata()).version(globalVersion).coordinationMetadata(CoordinationMetadata.builder(clusterState.coordinationMetadata()).term(oldTerm).build()).put(IndexMetadata.builder("test").version(// -1 because it's incremented in .put()
            indexMetadataVersion - 1).settings(Settings.builder().put(IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 1).put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 0).put(IndexMetadata.SETTING_INDEX_VERSION_CREATED.getKey(), Version.CURRENT).put(IndexMetadata.SETTING_INDEX_UUID, indexUUID)))).incrementVersion().build(), clusterState);
            clusterState = loadPersistedClusterState(persistedClusterStateService);
            IndexMetadata indexMetadata = clusterState.metadata().index("test");
            assertThat(indexMetadata.getIndexUUID(), equalTo(indexUUID));
            assertThat(indexMetadata.getVersion(), equalTo(indexMetadataVersion));
            assertThat(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(indexMetadata.getSettings()), equalTo(0));
            // ensure we do not wastefully persist the same index metadata version by making a bad update with the same version
            writer.writeIncrementalStateAndCommit(0L, clusterState, ClusterState.builder(clusterState).metadata(Metadata.builder(clusterState.metadata()).put(IndexMetadata.builder(indexMetadata).settings(Settings.builder().put(indexMetadata.getSettings()).put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 1)).build(), false)).incrementVersion().build());
            clusterState = loadPersistedClusterState(persistedClusterStateService);
            indexMetadata = clusterState.metadata().index("test");
            assertThat(indexMetadata.getIndexUUID(), equalTo(indexUUID));
            assertThat(indexMetadata.getVersion(), equalTo(indexMetadataVersion));
            assertThat(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(indexMetadata.getSettings()), equalTo(0));
            // ensure that we do persist the same index metadata version by making an update with a higher version
            writeState(writer, 0L, ClusterState.builder(clusterState).metadata(Metadata.builder(clusterState.metadata()).put(IndexMetadata.builder(indexMetadata).settings(Settings.builder().put(indexMetadata.getSettings()).put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 2)).build(), true)).incrementVersion().build(), clusterState);
            clusterState = loadPersistedClusterState(persistedClusterStateService);
            indexMetadata = clusterState.metadata().index("test");
            assertThat(indexMetadata.getVersion(), equalTo(indexMetadataVersion + 1));
            assertThat(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(indexMetadata.getSettings()), equalTo(2));
            // ensure that we also persist the index metadata when the term changes
            writeState(writer, 0L, ClusterState.builder(clusterState).metadata(Metadata.builder(clusterState.metadata()).coordinationMetadata(CoordinationMetadata.builder(clusterState.coordinationMetadata()).term(newTerm).build()).put(IndexMetadata.builder(indexMetadata).settings(Settings.builder().put(indexMetadata.getSettings()).put(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.getKey(), 3)).build(), false)).incrementVersion().build(), clusterState);
        }
        final ClusterState clusterState = loadPersistedClusterState(persistedClusterStateService);
        final IndexMetadata indexMetadata = clusterState.metadata().index("test");
        assertThat(indexMetadata.getIndexUUID(), equalTo(indexUUID));
        assertThat(indexMetadata.getVersion(), equalTo(indexMetadataVersion + 1));
        assertThat(IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING.get(indexMetadata.getSettings()), equalTo(3));
    }
}
Also used : ClusterState(org.opensearch.cluster.ClusterState) NodeEnvironment(org.opensearch.env.NodeEnvironment) Matchers.containsString(org.hamcrest.Matchers.containsString) IndexMetadata(org.opensearch.cluster.metadata.IndexMetadata) IndexWriter(org.apache.lucene.index.IndexWriter) Writer(org.opensearch.gateway.PersistedClusterStateService.Writer)

Example 18 with NodeEnvironment

use of org.opensearch.env.NodeEnvironment in project OpenSearch by opensearch-project.

the class PersistedClusterStateServiceTests method testFailsOnMismatchedNodeIds.

public void testFailsOnMismatchedNodeIds() throws IOException {
    final Path[] dataPaths1 = createDataPaths();
    final Path[] dataPaths2 = createDataPaths();
    final String[] nodeIds = new String[2];
    try (NodeEnvironment nodeEnvironment = newNodeEnvironment(dataPaths1)) {
        nodeIds[0] = nodeEnvironment.nodeId();
        try (Writer writer = newPersistedClusterStateService(nodeEnvironment).createWriter()) {
            final ClusterState clusterState = loadPersistedClusterState(newPersistedClusterStateService(nodeEnvironment));
            writer.writeFullStateAndCommit(0L, ClusterState.builder(clusterState).version(randomLongBetween(1L, Long.MAX_VALUE)).build());
        }
    }
    try (NodeEnvironment nodeEnvironment = newNodeEnvironment(dataPaths2)) {
        nodeIds[1] = nodeEnvironment.nodeId();
        try (Writer writer = newPersistedClusterStateService(nodeEnvironment).createWriter()) {
            final ClusterState clusterState = loadPersistedClusterState(newPersistedClusterStateService(nodeEnvironment));
            writer.writeFullStateAndCommit(0L, ClusterState.builder(clusterState).version(randomLongBetween(1L, Long.MAX_VALUE)).build());
        }
    }
    NodeMetadata.FORMAT.cleanupOldFiles(Long.MAX_VALUE, dataPaths2);
    final Path[] combinedPaths = Stream.concat(Arrays.stream(dataPaths1), Arrays.stream(dataPaths2)).toArray(Path[]::new);
    final String failure = expectThrows(IllegalStateException.class, () -> newNodeEnvironment(combinedPaths)).getMessage();
    assertThat(failure, allOf(containsString("unexpected node ID in metadata"), containsString(nodeIds[0]), containsString(nodeIds[1])));
    assertTrue("[" + failure + "] should match " + Arrays.toString(dataPaths2), Arrays.stream(dataPaths2).anyMatch(p -> failure.contains(p.toString())));
    // verify that loadBestOnDiskState has same check
    final String message = expectThrows(IllegalStateException.class, () -> new PersistedClusterStateService(Stream.of(combinedPaths).map(path -> NodeEnvironment.resolveNodePath(path, 0)).toArray(Path[]::new), nodeIds[0], xContentRegistry(), BigArrays.NON_RECYCLING_INSTANCE, new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), () -> 0L).loadBestOnDiskState()).getMessage();
    assertThat(message, allOf(containsString("unexpected node ID in metadata"), containsString(nodeIds[0]), containsString(nodeIds[1])));
    assertTrue("[" + message + "] should match " + Arrays.toString(dataPaths2), Arrays.stream(dataPaths2).anyMatch(p -> message.contains(p.toString())));
}
Also used : Path(java.nio.file.Path) Arrays(java.util.Arrays) Metadata(org.opensearch.cluster.metadata.Metadata) Term(org.apache.lucene.index.Term) NoneCircuitBreakerService(org.opensearch.indices.breaker.NoneCircuitBreakerService) Level(org.apache.logging.log4j.Level) Version(org.opensearch.Version) DiscoveryNode(org.opensearch.cluster.node.DiscoveryNode) NodeMetadata(org.opensearch.env.NodeMetadata) MockBigArrays(org.opensearch.common.util.MockBigArrays) Directory(org.apache.lucene.store.Directory) Matchers.nullValue(org.hamcrest.Matchers.nullValue) IOContext(org.apache.lucene.store.IOContext) MockPageCacheRecycler(org.opensearch.common.util.MockPageCacheRecycler) Path(java.nio.file.Path) NodeEnvironment(org.opensearch.env.NodeEnvironment) Index(org.opensearch.index.Index) Matchers.allOf(org.hamcrest.Matchers.allOf) OpenSearchTestCase(org.opensearch.test.OpenSearchTestCase) Collection(java.util.Collection) Settings(org.opensearch.common.settings.Settings) Collectors(java.util.stream.Collectors) IndexWriter(org.apache.lucene.index.IndexWriter) List(java.util.List) Logger(org.apache.logging.log4j.Logger) Stream(java.util.stream.Stream) Matchers.equalTo(org.hamcrest.Matchers.equalTo) BigArrays(org.opensearch.common.util.BigArrays) IndexWriterConfig(org.apache.lucene.index.IndexWriterConfig) Matchers.containsString(org.hamcrest.Matchers.containsString) DiscoveryNodes(org.opensearch.cluster.node.DiscoveryNodes) MockLogAppender(org.opensearch.test.MockLogAppender) IndexMetadata(org.opensearch.cluster.metadata.IndexMetadata) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) CoordinationMetadata(org.opensearch.cluster.coordination.CoordinationMetadata) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) ClusterState(org.opensearch.cluster.ClusterState) Matchers.lessThan(org.hamcrest.Matchers.lessThan) UUIDs(org.opensearch.common.UUIDs) ClusterSettings(org.opensearch.common.settings.ClusterSettings) IndexOutput(org.apache.lucene.store.IndexOutput) Environment(org.opensearch.env.Environment) IOException(java.io.IOException) IOError(java.io.IOError) IOUtils(org.opensearch.core.internal.io.IOUtils) TestLogging(org.opensearch.test.junit.annotations.TestLogging) AtomicLong(java.util.concurrent.atomic.AtomicLong) FilterDirectory(org.apache.lucene.store.FilterDirectory) NIOFSDirectory(org.apache.lucene.store.NIOFSDirectory) ClusterName(org.opensearch.cluster.ClusterName) Writer(org.opensearch.gateway.PersistedClusterStateService.Writer) LogManager(org.apache.logging.log4j.LogManager) ClusterState(org.opensearch.cluster.ClusterState) ClusterSettings(org.opensearch.common.settings.ClusterSettings) NodeEnvironment(org.opensearch.env.NodeEnvironment) Matchers.containsString(org.hamcrest.Matchers.containsString) IndexWriter(org.apache.lucene.index.IndexWriter) Writer(org.opensearch.gateway.PersistedClusterStateService.Writer)

Example 19 with NodeEnvironment

use of org.opensearch.env.NodeEnvironment in project OpenSearch by opensearch-project.

the class PersistedClusterStateServiceTests method testClosesWriterOnFatalError.

public void testClosesWriterOnFatalError() throws IOException {
    final AtomicBoolean throwException = new AtomicBoolean();
    try (NodeEnvironment nodeEnvironment = newNodeEnvironment(createDataPaths())) {
        final PersistedClusterStateService persistedClusterStateService = new PersistedClusterStateService(nodeEnvironment, xContentRegistry(), getBigArrays(), new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), () -> 0L) {

            @Override
            Directory createDirectory(Path path) throws IOException {
                return new FilterDirectory(super.createDirectory(path)) {

                    @Override
                    public void sync(Collection<String> names) {
                        throw new OutOfMemoryError("simulated");
                    }
                };
            }
        };
        try (Writer writer = persistedClusterStateService.createWriter()) {
            final ClusterState clusterState = loadPersistedClusterState(persistedClusterStateService);
            final long newTerm = randomNonNegativeLong();
            final ClusterState newState = ClusterState.builder(clusterState).metadata(Metadata.builder(clusterState.metadata()).clusterUUID(UUIDs.randomBase64UUID(random())).clusterUUIDCommitted(true).version(randomLongBetween(1L, Long.MAX_VALUE))).incrementVersion().build();
            throwException.set(true);
            assertThat(expectThrows(OutOfMemoryError.class, () -> {
                if (randomBoolean()) {
                    writeState(writer, newTerm, newState, clusterState);
                } else {
                    writer.commit(newTerm, newState.version());
                }
            }).getMessage(), containsString("simulated"));
            assertFalse(writer.isOpen());
        }
        // check if we can open writer again
        try (Writer ignored = persistedClusterStateService.createWriter()) {
        }
    }
}
Also used : Path(java.nio.file.Path) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) ClusterState(org.opensearch.cluster.ClusterState) ClusterSettings(org.opensearch.common.settings.ClusterSettings) NodeEnvironment(org.opensearch.env.NodeEnvironment) FilterDirectory(org.apache.lucene.store.FilterDirectory) Collection(java.util.Collection) IndexWriter(org.apache.lucene.index.IndexWriter) Writer(org.opensearch.gateway.PersistedClusterStateService.Writer)

Example 20 with NodeEnvironment

use of org.opensearch.env.NodeEnvironment in project OpenSearch by opensearch-project.

the class GatewayMetaStatePersistedStateTests method testStatePersistenceWithIOIssues.

public void testStatePersistenceWithIOIssues() throws IOException {
    final AtomicReference<Double> ioExceptionRate = new AtomicReference<>(0.01d);
    final List<MockDirectoryWrapper> list = new ArrayList<>();
    final PersistedClusterStateService persistedClusterStateService = new PersistedClusterStateService(nodeEnvironment, xContentRegistry(), getBigArrays(), new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), () -> 0L) {

        @Override
        Directory createDirectory(Path path) {
            final MockDirectoryWrapper wrapper = newMockFSDirectory(path);
            wrapper.setAllowRandomFileNotFoundException(randomBoolean());
            wrapper.setRandomIOExceptionRate(ioExceptionRate.get());
            wrapper.setRandomIOExceptionRateOnOpen(ioExceptionRate.get());
            list.add(wrapper);
            return wrapper;
        }
    };
    ClusterState state = createClusterState(randomNonNegativeLong(), Metadata.builder().clusterUUID(randomAlphaOfLength(10)).build());
    long currentTerm = 42L;
    try (GatewayMetaState.LucenePersistedState persistedState = new GatewayMetaState.LucenePersistedState(persistedClusterStateService, currentTerm, state)) {
        try {
            if (randomBoolean()) {
                final ClusterState newState = createClusterState(randomNonNegativeLong(), Metadata.builder().clusterUUID(randomAlphaOfLength(10)).build());
                persistedState.setLastAcceptedState(newState);
                state = newState;
            } else {
                final long newTerm = currentTerm + 1;
                persistedState.setCurrentTerm(newTerm);
                currentTerm = newTerm;
            }
        } catch (IOError | Exception e) {
            assertNotNull(ExceptionsHelper.unwrap(e, IOException.class));
        }
        ioExceptionRate.set(0.0d);
        for (MockDirectoryWrapper wrapper : list) {
            wrapper.setRandomIOExceptionRate(ioExceptionRate.get());
            wrapper.setRandomIOExceptionRateOnOpen(ioExceptionRate.get());
        }
        for (int i = 0; i < randomIntBetween(1, 5); i++) {
            if (randomBoolean()) {
                final long version = randomNonNegativeLong();
                final String indexName = randomAlphaOfLength(10);
                final IndexMetadata indexMetadata = createIndexMetadata(indexName, randomIntBetween(1, 5), randomNonNegativeLong());
                final Metadata metadata = Metadata.builder().persistentSettings(Settings.builder().put(randomAlphaOfLength(10), randomAlphaOfLength(10)).build()).coordinationMetadata(createCoordinationMetadata(1L)).put(indexMetadata, false).build();
                state = createClusterState(version, metadata);
                persistedState.setLastAcceptedState(state);
            } else {
                currentTerm += 1;
                persistedState.setCurrentTerm(currentTerm);
            }
        }
        assertEquals(state, persistedState.getLastAcceptedState());
        assertEquals(currentTerm, persistedState.getCurrentTerm());
    } catch (IOError | Exception e) {
        if (ioExceptionRate.get() == 0.0d) {
            throw e;
        }
        assertNotNull(ExceptionsHelper.unwrap(e, IOException.class));
        return;
    }
    nodeEnvironment.close();
    // verify that the freshest state was rewritten to each data path
    for (Path path : nodeEnvironment.nodeDataPaths()) {
        Settings settings = Settings.builder().put(Environment.PATH_HOME_SETTING.getKey(), createTempDir().toAbsolutePath()).put(Environment.PATH_DATA_SETTING.getKey(), path.getParent().getParent().toString()).build();
        try (NodeEnvironment nodeEnvironment = new NodeEnvironment(settings, TestEnvironment.newEnvironment(settings))) {
            final PersistedClusterStateService newPersistedClusterStateService = new PersistedClusterStateService(nodeEnvironment, xContentRegistry(), getBigArrays(), new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS), () -> 0L);
            final PersistedClusterStateService.OnDiskState onDiskState = newPersistedClusterStateService.loadBestOnDiskState();
            assertFalse(onDiskState.empty());
            assertThat(onDiskState.currentTerm, equalTo(currentTerm));
            assertClusterStateEqual(state, ClusterState.builder(ClusterName.DEFAULT).version(onDiskState.lastAcceptedVersion).metadata(onDiskState.metadata).build());
        }
    }
}
Also used : MockDirectoryWrapper(org.apache.lucene.store.MockDirectoryWrapper) Path(java.nio.file.Path) ClusterState(org.opensearch.cluster.ClusterState) ClusterSettings(org.opensearch.common.settings.ClusterSettings) NodeEnvironment(org.opensearch.env.NodeEnvironment) ArrayList(java.util.ArrayList) Metadata(org.opensearch.cluster.metadata.Metadata) IndexMetadata(org.opensearch.cluster.metadata.IndexMetadata) CoordinationMetadata(org.opensearch.cluster.coordination.CoordinationMetadata) AtomicReference(java.util.concurrent.atomic.AtomicReference) IOException(java.io.IOException) IOError(java.io.IOError) IndexMetadata(org.opensearch.cluster.metadata.IndexMetadata) ClusterSettings(org.opensearch.common.settings.ClusterSettings) Settings(org.opensearch.common.settings.Settings)

Aggregations

NodeEnvironment (org.opensearch.env.NodeEnvironment)62 Settings (org.opensearch.common.settings.Settings)36 Path (java.nio.file.Path)32 Matchers.containsString (org.hamcrest.Matchers.containsString)22 ClusterState (org.opensearch.cluster.ClusterState)21 IndexMetadata (org.opensearch.cluster.metadata.IndexMetadata)19 ClusterSettings (org.opensearch.common.settings.ClusterSettings)17 IndexWriter (org.apache.lucene.index.IndexWriter)16 Metadata (org.opensearch.cluster.metadata.Metadata)16 Writer (org.opensearch.gateway.PersistedClusterStateService.Writer)16 Index (org.opensearch.index.Index)13 IOException (java.io.IOException)12 FilterDirectory (org.apache.lucene.store.FilterDirectory)9 Environment (org.opensearch.env.Environment)9 ArrayList (java.util.ArrayList)7 Directory (org.apache.lucene.store.Directory)7 TestThreadPool (org.opensearch.threadpool.TestThreadPool)7 FileSystem (java.nio.file.FileSystem)6 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)6 IndexWriterConfig (org.apache.lucene.index.IndexWriterConfig)6