This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch snapshot/2.0.0-250109 in repository https://gitbox.apache.org/repos/asf/tsfile.git
commit dfc384f9805fec170151fb056170498578c93489 Author: JackieTien97 <[email protected]> AuthorDate: Thu Jan 9 14:38:12 2025 +0800 Revert "add cache table schema map option (#369)" This reverts commit 1ad55931758e5ad86ca4882958c83f6dcbf78723. --- .../tsfile/compatibility/CompatibilityUtils.java | 2 -- .../tsfile/compatibility/DeserializeConfig.java | 4 +-- .../tsfile/file/metadata/TsFileMetadata.java | 26 ++------------- .../apache/tsfile/read/TsFileSequenceReader.java | 39 ++-------------------- .../tsfile/read/controller/IMetadataQuerier.java | 3 -- .../read/controller/MetadataQuerierByFileImpl.java | 9 ----- .../read/query/executor/TableQueryExecutor.java | 2 +- .../tsfile/read/v4/DeviceTableModelReader.java | 10 +++--- .../org/apache/tsfile/utils/TsFileSketchTool.java | 9 +++-- .../tsfile/file/metadata/TsFileMetadataTest.java | 2 +- .../tsfile/read/TsFileSequenceReaderTest.java | 37 -------------------- .../apache/tsfile/write/TsFileWriteApiTest.java | 2 +- 12 files changed, 22 insertions(+), 123 deletions(-) diff --git a/java/tsfile/src/main/java/org/apache/tsfile/compatibility/CompatibilityUtils.java b/java/tsfile/src/main/java/org/apache/tsfile/compatibility/CompatibilityUtils.java index f8d2ca5d..ec0102c5 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/compatibility/CompatibilityUtils.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/compatibility/CompatibilityUtils.java @@ -42,8 +42,6 @@ public class CompatibilityUtils { org.apache.tsfile.common.conf.TSFileConfig.VERSION_NUMBER_V3; v3DeserializeConfig.tsFileMetadataBufferDeserializer = CompatibilityUtils::deserializeTsFileMetadataFromV3; - v3DeserializeConfig.cacheTableSchemaMapTsFileMetadataBufferDeserializer = - CompatibilityUtils::deserializeTsFileMetadataFromV3; v3DeserializeConfig.deviceIDBufferDeserializer = ((buffer, context) -> { final PlainDeviceID deviceID = PlainDeviceID.deserialize(buffer); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/compatibility/DeserializeConfig.java b/java/tsfile/src/main/java/org/apache/tsfile/compatibility/DeserializeConfig.java index c11225f9..b9228bd0 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/compatibility/DeserializeConfig.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/compatibility/DeserializeConfig.java @@ -36,9 +36,7 @@ import java.nio.ByteBuffer; public class DeserializeConfig { public byte versionNumber = org.apache.tsfile.common.conf.TSFileConfig.VERSION_NUMBER; public BufferDeserializer<TsFileMetadata> tsFileMetadataBufferDeserializer = - TsFileMetadata::deserializeWithoutCacheTableSchemaMap; - public BufferDeserializer<TsFileMetadata> cacheTableSchemaMapTsFileMetadataBufferDeserializer = - TsFileMetadata::deserializeAndCacheTableSchemaMap; + TsFileMetadata::deserializeFrom; public BufferDeserializer<MetadataIndexNode> deviceMetadataIndexNodeBufferDeserializer = (buffer, context) -> MetadataIndexNode.deserializeFrom(buffer, true, context); diff --git a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java index d7ac19bb..16f1da5d 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/file/metadata/TsFileMetadata.java @@ -46,7 +46,6 @@ public class TsFileMetadata { // List of <name, offset, childMetadataIndexType> private Map<String, MetadataIndexNode> tableMetadataIndexNodeMap; private Map<String, TableSchema> tableSchemaMap; - private boolean hasTableSchemaMapCache; private Map<String, String> tsFileProperties; // offset of MetaMarker.SEPARATOR @@ -58,24 +57,13 @@ public class TsFileMetadata { private String encryptType; - public static TsFileMetadata deserializeAndCacheTableSchemaMap( - ByteBuffer buffer, DeserializeConfig context) { - return deserializeFrom(buffer, context, true); - } - - public static TsFileMetadata deserializeWithoutCacheTableSchemaMap( - ByteBuffer buffer, DeserializeConfig context) { - return deserializeFrom(buffer, context, false); - } - /** * deserialize data from the buffer. * * @param buffer -buffer use to deserialize * @return -an instance of TsFileMetaData */ - public static TsFileMetadata deserializeFrom( - ByteBuffer buffer, DeserializeConfig context, boolean needTableSchemaMap) { + public static TsFileMetadata deserializeFrom(ByteBuffer buffer, DeserializeConfig context) { TsFileMetadata fileMetaData = new TsFileMetadata(); int startPos = buffer.position(); @@ -96,13 +84,10 @@ public class TsFileMetadata { for (int i = 0; i < tableSchemaNum; i++) { String tableName = ReadWriteIOUtils.readVarIntString(buffer); TableSchema tableSchema = context.tableSchemaBufferDeserializer.deserialize(buffer, context); - if (needTableSchemaMap) { - tableSchema.setTableName(tableName); - tableSchemaMap.put(tableName, tableSchema); - } + tableSchema.setTableName(tableName); + tableSchemaMap.put(tableName, tableSchema); } fileMetaData.setTableSchemaMap(tableSchemaMap); - fileMetaData.hasTableSchemaMapCache = needTableSchemaMap; // metaOffset long metaOffset = ReadWriteIOUtils.readLong(buffer); @@ -282,7 +267,6 @@ public class TsFileMetadata { public void setTableSchemaMap(Map<String, TableSchema> tableSchemaMap) { this.tableSchemaMap = tableSchemaMap; - this.hasTableSchemaMapCache = true; } public Map<String, MetadataIndexNode> getTableMetadataIndexNodeMap() { @@ -297,10 +281,6 @@ public class TsFileMetadata { return metadataIndexNode; } - public boolean hasTableSchemaMapCache() { - return hasTableSchemaMapCache; - } - public Map<String, TableSchema> getTableSchemaMap() { return tableSchemaMap; } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java index 3f7a171b..127630f3 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/TsFileSequenceReader.java @@ -22,7 +22,6 @@ package org.apache.tsfile.read; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.common.conf.TSFileDescriptor; import org.apache.tsfile.common.constant.TsFileConstant; -import org.apache.tsfile.compatibility.BufferDeserializer; import org.apache.tsfile.compatibility.CompatibilityUtils; import org.apache.tsfile.compatibility.DeserializeConfig; import org.apache.tsfile.compress.IUnCompressor; @@ -53,7 +52,6 @@ import org.apache.tsfile.file.metadata.ITimeSeriesMetadata; import org.apache.tsfile.file.metadata.MeasurementMetadataIndexEntry; import org.apache.tsfile.file.metadata.MetadataIndexNode; import org.apache.tsfile.file.metadata.TableDeviceMetadata; -import org.apache.tsfile.file.metadata.TableSchema; import org.apache.tsfile.file.metadata.TimeseriesMetadata; import org.apache.tsfile.file.metadata.TsFileMetadata; import org.apache.tsfile.file.metadata.enums.CompressionType; @@ -136,7 +134,6 @@ public class TsFileSequenceReader implements AutoCloseable { private byte fileVersion; private DeserializeConfig deserializeConfig = new DeserializeConfig(); - private volatile boolean cacheTableSchemaMap = false; /** * Create a file reader of the given file. The reader will read the tail of the file to get the @@ -288,10 +285,6 @@ public class TsFileSequenceReader implements AutoCloseable { } } - public void setEnableCacheTableSchemaMap() { - this.cacheTableSchemaMap = true; - } - public void loadMetadataSize() throws IOException { loadMetadataSize(null); } @@ -401,7 +394,9 @@ public class TsFileSequenceReader implements AutoCloseable { if (tsFileMetaData == null) { synchronized (this) { if (tsFileMetaData == null) { - tsFileMetaData = forceReadFileMetadata(cacheTableSchemaMap, ioSizeRecorder); + tsFileMetaData = + deserializeConfig.tsFileMetadataBufferDeserializer.deserialize( + readData(fileMetadataPos, fileMetadataSize, ioSizeRecorder), deserializeConfig); } } } @@ -414,34 +409,6 @@ public class TsFileSequenceReader implements AutoCloseable { return tsFileMetaData; } - public Map<String, TableSchema> getTableSchemaMap() throws IOException { - return getTableSchemaMap(null); - } - - public Map<String, TableSchema> getTableSchemaMap(LongConsumer ioSizeRecorder) - throws IOException { - if (tsFileMetaData != null && tsFileMetaData.hasTableSchemaMapCache()) { - return tsFileMetaData.getTableSchemaMap(); - } - TsFileMetadata tempTsFileMetadata = forceReadFileMetadata(true, ioSizeRecorder); - if (cacheTableSchemaMap) { - synchronized (this) { - this.tsFileMetaData = tempTsFileMetadata; - } - } - return tempTsFileMetadata.getTableSchemaMap(); - } - - private TsFileMetadata forceReadFileMetadata( - boolean needTableSchemaMap, LongConsumer ioSizeRecorder) throws IOException { - ByteBuffer buffer = readData(fileMetadataPos, fileMetadataSize, ioSizeRecorder); - BufferDeserializer<TsFileMetadata> deserializer = - needTableSchemaMap - ? deserializeConfig.cacheTableSchemaMapTsFileMetadataBufferDeserializer - : deserializeConfig.tsFileMetadataBufferDeserializer; - return deserializer.deserialize(buffer, deserializeConfig); - } - /** * This function does not modify the position of the file reader. * diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/controller/IMetadataQuerier.java b/java/tsfile/src/main/java/org/apache/tsfile/read/controller/IMetadataQuerier.java index 3503e49e..b6d9654a 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/controller/IMetadataQuerier.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/controller/IMetadataQuerier.java @@ -24,7 +24,6 @@ import org.apache.tsfile.exception.write.NoMeasurementException; import org.apache.tsfile.file.metadata.IChunkMetadata; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.MetadataIndexNode; -import org.apache.tsfile.file.metadata.TableSchema; import org.apache.tsfile.file.metadata.TsFileMetadata; import org.apache.tsfile.read.common.Path; import org.apache.tsfile.read.common.TimeRange; @@ -56,8 +55,6 @@ public interface IMetadataQuerier { TsFileMetadata getWholeFileMetadata(); - Map<String, TableSchema> getTableSchemaMap(); - /** * this will load all chunk metadata of given paths into cache. * diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/controller/MetadataQuerierByFileImpl.java b/java/tsfile/src/main/java/org/apache/tsfile/read/controller/MetadataQuerierByFileImpl.java index ae7fb9ee..47888d01 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/controller/MetadataQuerierByFileImpl.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/controller/MetadataQuerierByFileImpl.java @@ -27,7 +27,6 @@ import org.apache.tsfile.file.metadata.IChunkMetadata; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.ITimeSeriesMetadata; import org.apache.tsfile.file.metadata.MetadataIndexNode; -import org.apache.tsfile.file.metadata.TableSchema; import org.apache.tsfile.file.metadata.TimeseriesMetadata; import org.apache.tsfile.file.metadata.TsFileMetadata; import org.apache.tsfile.read.TsFileSequenceReader; @@ -61,14 +60,11 @@ public class MetadataQuerierByFileImpl implements IMetadataQuerier { private LRUCache<Pair<IDeviceID, String>, List<IChunkMetadata>> deviceIdChunkMetadataCache; private TsFileSequenceReader tsFileReader; - private Map<String, TableSchema> tableSchemaMap; /** Constructor of MetadataQuerierByFileImpl. */ public MetadataQuerierByFileImpl(TsFileSequenceReader tsFileReader) throws IOException { this.tsFileReader = tsFileReader; - this.tsFileReader.setEnableCacheTableSchemaMap(); this.fileMetaData = tsFileReader.readFileMetadata(); - this.tableSchemaMap = tsFileReader.getTableSchemaMap(); deviceIdChunkMetadataCache = new LRUCache<Pair<IDeviceID, String>, List<IChunkMetadata>>(CACHED_ENTRY_NUMBER) { @Override @@ -131,11 +127,6 @@ public class MetadataQuerierByFileImpl implements IMetadataQuerier { return fileMetaData; } - @Override - public Map<String, TableSchema> getTableSchemaMap() { - return tableSchemaMap; - } - @Override @SuppressWarnings("squid:S3776") // Suppress high Cognitive Complexity warning public void loadChunkMetaDatas(List<Path> paths) throws IOException { diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/query/executor/TableQueryExecutor.java b/java/tsfile/src/main/java/org/apache/tsfile/read/query/executor/TableQueryExecutor.java index 8d178aa4..82c59890 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/query/executor/TableQueryExecutor.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/query/executor/TableQueryExecutor.java @@ -76,7 +76,7 @@ public class TableQueryExecutor { throws ReadProcessException { TsFileMetadata fileMetadata = metadataQuerier.getWholeFileMetadata(); MetadataIndexNode tableRoot = fileMetadata.getTableMetadataIndexNode(tableName); - TableSchema tableSchema = metadataQuerier.getTableSchemaMap().get(tableName); + TableSchema tableSchema = fileMetadata.getTableSchemaMap().get(tableName); if (tableRoot == null || tableSchema == null) { return new EmptyTsBlockReader(); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/read/v4/DeviceTableModelReader.java b/java/tsfile/src/main/java/org/apache/tsfile/read/v4/DeviceTableModelReader.java index 894353fa..f9c9870a 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/read/v4/DeviceTableModelReader.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/read/v4/DeviceTableModelReader.java @@ -25,6 +25,7 @@ import org.apache.tsfile.exception.read.ReadProcessException; import org.apache.tsfile.exception.write.NoMeasurementException; import org.apache.tsfile.exception.write.NoTableException; import org.apache.tsfile.file.metadata.TableSchema; +import org.apache.tsfile.file.metadata.TsFileMetadata; import org.apache.tsfile.read.TsFileSequenceReader; import org.apache.tsfile.read.controller.CachedChunkLoaderImpl; import org.apache.tsfile.read.controller.IChunkLoader; @@ -56,7 +57,6 @@ public class DeviceTableModelReader implements ITsFileReader { public DeviceTableModelReader(File file) throws IOException { this.fileReader = new TsFileSequenceReader(file.getPath()); - this.fileReader.setEnableCacheTableSchemaMap(); this.metadataQuerier = new MetadataQuerierByFileImpl(fileReader); this.chunkLoader = new CachedChunkLoaderImpl(fileReader); this.queryExecutor = @@ -66,13 +66,14 @@ public class DeviceTableModelReader implements ITsFileReader { @TsFileApi public List<TableSchema> getAllTableSchema() throws IOException { - Map<String, TableSchema> tableSchemaMap = fileReader.getTableSchemaMap(); + Map<String, TableSchema> tableSchemaMap = fileReader.readFileMetadata().getTableSchemaMap(); return new ArrayList<>(tableSchemaMap.values()); } @TsFileApi public Optional<TableSchema> getTableSchemas(String tableName) throws IOException { - Map<String, TableSchema> tableSchemaMap = fileReader.getTableSchemaMap(); + TsFileMetadata tsFileMetadata = fileReader.readFileMetadata(); + Map<String, TableSchema> tableSchemaMap = tsFileMetadata.getTableSchemaMap(); return Optional.ofNullable(tableSchemaMap.get(tableName.toLowerCase())); } @@ -80,7 +81,8 @@ public class DeviceTableModelReader implements ITsFileReader { public ResultSet query(String tableName, List<String> columnNames, long startTime, long endTime) throws IOException, NoTableException, NoMeasurementException, ReadProcessException { String lowerCaseTableName = tableName.toLowerCase(); - TableSchema tableSchema = fileReader.getTableSchemaMap().get(lowerCaseTableName); + TsFileMetadata tsFileMetadata = fileReader.readFileMetadata(); + TableSchema tableSchema = tsFileMetadata.getTableSchemaMap().get(lowerCaseTableName); if (tableSchema == null) { throw new NoTableException(tableName); } diff --git a/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileSketchTool.java b/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileSketchTool.java index 2bf2fd5e..e7ce355a 100644 --- a/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileSketchTool.java +++ b/java/tsfile/src/main/java/org/apache/tsfile/utils/TsFileSketchTool.java @@ -202,10 +202,13 @@ public class TsFileSketchTool { } // table schema - Map<String, TableSchema> tableSchemaMap = reader.getTableSchemaMap(); - printlnBoth(pw, String.format("%20s", pos) + "|\tTableSchemaCnt=" + tableSchemaMap.size()); + printlnBoth( + pw, + String.format("%20s", pos) + + "|\tTableSchemaCnt=" + + tsFileMetaData.getTableSchemaMap().size()); pos += Integer.BYTES; - for (Entry<String, TableSchema> entry : tableSchemaMap.entrySet()) { + for (Entry<String, TableSchema> entry : tsFileMetaData.getTableSchemaMap().entrySet()) { final String tableName = entry.getKey(); final TableSchema tableSchema = entry.getValue(); diff --git a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/TsFileMetadataTest.java b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/TsFileMetadataTest.java index dcf34391..03e22e27 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/TsFileMetadataTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/file/metadata/TsFileMetadataTest.java @@ -72,7 +72,7 @@ public class TsFileMetadataTest { ByteBuffer buffer = ByteBuffer.allocate((int) channel.size()); channel.read(buffer); buffer.rewind(); - metaData = TsFileMetadata.deserializeAndCacheTableSchemaMap(buffer, deserializeConfig); + metaData = TsFileMetadata.deserializeFrom(buffer, deserializeConfig); return metaData; } catch (IOException e) { e.printStackTrace(); diff --git a/java/tsfile/src/test/java/org/apache/tsfile/read/TsFileSequenceReaderTest.java b/java/tsfile/src/test/java/org/apache/tsfile/read/TsFileSequenceReaderTest.java index 854e80ac..14e97de3 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/read/TsFileSequenceReaderTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/read/TsFileSequenceReaderTest.java @@ -32,7 +32,6 @@ import org.apache.tsfile.file.metadata.ChunkMetadata; import org.apache.tsfile.file.metadata.IChunkMetadata; import org.apache.tsfile.file.metadata.IDeviceID; import org.apache.tsfile.file.metadata.IDeviceID.Factory; -import org.apache.tsfile.file.metadata.TableSchema; import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.apache.tsfile.read.common.Path; import org.apache.tsfile.utils.BloomFilter; @@ -41,13 +40,10 @@ import org.apache.tsfile.utils.Pair; import org.apache.tsfile.utils.TsFileGeneratorUtils; import org.apache.tsfile.write.TsFileWriter; import org.apache.tsfile.write.record.TSRecord; -import org.apache.tsfile.write.record.Tablet; import org.apache.tsfile.write.record.datapoint.DoubleDataPoint; import org.apache.tsfile.write.schema.IMeasurementSchema; import org.apache.tsfile.write.schema.MeasurementSchema; import org.apache.tsfile.write.schema.Schema; -import org.apache.tsfile.write.v4.ITsFileWriter; -import org.apache.tsfile.write.v4.TsFileWriterBuilder; import org.junit.After; import org.junit.Assert; @@ -57,9 +53,7 @@ import org.junit.Test; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.file.Files; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -231,35 +225,4 @@ public class TsFileSequenceReaderTest { reader.selfCheck(new Schema(), new ArrayList<>(), false)); } } - - @Test - public void testGetTableSchemaMap() throws IOException, WriteProcessException { - File file = new File(FILE_PATH); - try { - tsFile.close(); - Files.deleteIfExists(file.toPath()); - } catch (IOException ignored) { - } - TableSchema tableSchema = - new TableSchema( - "t1", - Collections.singletonList(new MeasurementSchema("s1", TSDataType.INT32)), - Collections.singletonList(Tablet.ColumnCategory.FIELD)); - try (ITsFileWriter writer = - new TsFileWriterBuilder().tableSchema(tableSchema).file(file).build()) { - Tablet tablet = - new Tablet(Collections.singletonList("s1"), Collections.singletonList(TSDataType.INT32)); - tablet.addTimestamp(0, 1); - tablet.addValue("s1", 0, 1); - writer.write(tablet); - } - try (TsFileSequenceReader reader = new TsFileSequenceReader(FILE_PATH)) { - Assert.assertFalse(reader.readFileMetadata().hasTableSchemaMapCache()); - Assert.assertEquals(1, reader.getTableSchemaMap().size()); - Assert.assertFalse(reader.readFileMetadata().hasTableSchemaMapCache()); - reader.setEnableCacheTableSchemaMap(); - Assert.assertEquals(1, reader.getTableSchemaMap().size()); - Assert.assertTrue(reader.readFileMetadata().hasTableSchemaMapCache()); - } - } } diff --git a/java/tsfile/src/test/java/org/apache/tsfile/write/TsFileWriteApiTest.java b/java/tsfile/src/test/java/org/apache/tsfile/write/TsFileWriteApiTest.java index 914e25b3..0a4d92f1 100644 --- a/java/tsfile/src/test/java/org/apache/tsfile/write/TsFileWriteApiTest.java +++ b/java/tsfile/src/test/java/org/apache/tsfile/write/TsFileWriteApiTest.java @@ -937,7 +937,7 @@ public class TsFileWriteApiTest { writer.writeTable(tablet); } try (TsFileSequenceReader reader = new TsFileSequenceReader(f.getPath())) { - Map<String, TableSchema> tableSchemaMap = reader.getTableSchemaMap(); + Map<String, TableSchema> tableSchemaMap = reader.readFileMetadata().getTableSchemaMap(); TableSchema tableSchemaInTsFile = tableSchemaMap.get("table1"); Assert.assertNotNull(tableSchemaInTsFile); for (IMeasurementSchema columnSchema : tableSchemaInTsFile.getColumnSchemas()) {
