From 91061a6b02dfa0a615b8a3cafc48c81d4ac9f044 Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 16:43:33 +0800 Subject: [PATCH 01/12] [IOTDB-1583] Raft log failed to be committed in cluster version --- .../cluster/server/member/RaftMember.java | 19 ++++++++++++++ .../writelog/node/ExclusiveWriteLogNode.java | 3 +++ .../iotdb/db/writelog/WriteLogNodeTest.java | 26 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index a5a58b0ea52b7..56dd644319588 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -78,6 +78,7 @@ import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSStatus; +import org.apache.iotdb.tsfile.utils.RamUsageEstimator; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.thrift.TException; @@ -116,6 +117,7 @@ */ @SuppressWarnings("java:S3077") // reference volatile is enough public abstract class RaftMember { + private static final Logger logger = LoggerFactory.getLogger(RaftMember.class); public static final boolean USE_LOG_DISPATCHER = false; @@ -1032,6 +1034,15 @@ TSStatus processPlanLocally(PhysicalPlan plan) { log.setCurrLogTerm(getTerm().get()); log.setCurrLogIndex(logManager.getLastLogIndex() + 1); + // if a single log exceeds the threshold + // we need to return error code to the client as in server mode + if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { + logger.error( + "Log cannot fit into buffer, please increase raft_log_buffer_size;" + + "or reduce the size of requests you send."); + return StatusUtils.INTERNAL_ERROR; + } logManager.append(log); } Timer.Statistic.RAFT_SENDER_APPEND_LOG.calOperationCostTimeFromStart(startTime); @@ -1077,6 +1088,14 @@ private TSStatus processPlanLocallyV2(PhysicalPlan plan) { log.setCurrLogIndex(logManager.getLastLogIndex() + 1); startTime = Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.getOperationStartTime(); + // just like processPlanLocally,we need to check the size of log + if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { + logger.error( + "Log cannot fit into buffer, please increase raft_log_buffer_size;" + + "or reduce the size of requests you send."); + return StatusUtils.INTERNAL_ERROR; + } // logDispatcher will serialize log, and set log size, and we will use the size after it logManager.append(log); Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.calOperationCostTimeFromStart(startTime); diff --git a/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java b/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java index 56fafe2f86641..e11a11eb59d43 100644 --- a/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java +++ b/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java @@ -113,6 +113,9 @@ public void write(PhysicalPlan plan) throws IOException { sync(); } } catch (BufferOverflowException e) { + // if the size of a single plan bigger than logBufferWorking + // we need to clear the buffer to drop something wrong that has written. + logBufferWorking.clear(); throw new IOException("Log cannot fit into the buffer, please increase wal_buffer_size", e); } finally { lock.unlock(); diff --git a/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java b/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java index 2767813b39ad3..fd7cd0d95c4a6 100644 --- a/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java @@ -316,6 +316,32 @@ public void testOverSizedWAL() throws IOException, IllegalPathException { } assertTrue(caught); + // if last insertplan failed by overflow,it can not take affect the next insertplan + InsertRowPlan bwInsertPlan2 = + new InsertRowPlan( + new PartialPath("root.logTestDevice.oversize"), + 100, + new String[] {"s1", "s2", "s3", "s4"}, + new TSDataType[] { + TSDataType.DOUBLE, TSDataType.INT64, TSDataType.TEXT, TSDataType.BOOLEAN + }, + // try to apply a insertplan whose size will fill the entire logBufferWorking + new String[] { + "1.0", + "15", + new String( + new char + [(IoTDBDescriptor.getInstance().getConfig().getWalBufferSize() / 2) - 109]), + "false" + }); + caught = false; + try { + logNode.write(bwInsertPlan2); + } catch (IOException e) { + caught = true; + } + assertFalse(caught); + ByteBuffer[] array = logNode.delete(); for (ByteBuffer byteBuffer : array) { MmapUtil.clean((MappedByteBuffer) byteBuffer); From dda6da645dc4355b2e008b76b9e71e7e913ad7be Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 16:43:33 +0800 Subject: [PATCH 02/12] [IOTDB-1583] Raft log failed to be committed in cluster version --- .../cluster/server/member/RaftMember.java | 19 ++++++++++++++ .../writelog/node/ExclusiveWriteLogNode.java | 3 +++ .../iotdb/db/writelog/WriteLogNodeTest.java | 26 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index a5a58b0ea52b7..56dd644319588 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -78,6 +78,7 @@ import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSStatus; +import org.apache.iotdb.tsfile.utils.RamUsageEstimator; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.thrift.TException; @@ -116,6 +117,7 @@ */ @SuppressWarnings("java:S3077") // reference volatile is enough public abstract class RaftMember { + private static final Logger logger = LoggerFactory.getLogger(RaftMember.class); public static final boolean USE_LOG_DISPATCHER = false; @@ -1032,6 +1034,15 @@ TSStatus processPlanLocally(PhysicalPlan plan) { log.setCurrLogTerm(getTerm().get()); log.setCurrLogIndex(logManager.getLastLogIndex() + 1); + // if a single log exceeds the threshold + // we need to return error code to the client as in server mode + if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { + logger.error( + "Log cannot fit into buffer, please increase raft_log_buffer_size;" + + "or reduce the size of requests you send."); + return StatusUtils.INTERNAL_ERROR; + } logManager.append(log); } Timer.Statistic.RAFT_SENDER_APPEND_LOG.calOperationCostTimeFromStart(startTime); @@ -1077,6 +1088,14 @@ private TSStatus processPlanLocallyV2(PhysicalPlan plan) { log.setCurrLogIndex(logManager.getLastLogIndex() + 1); startTime = Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.getOperationStartTime(); + // just like processPlanLocally,we need to check the size of log + if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { + logger.error( + "Log cannot fit into buffer, please increase raft_log_buffer_size;" + + "or reduce the size of requests you send."); + return StatusUtils.INTERNAL_ERROR; + } // logDispatcher will serialize log, and set log size, and we will use the size after it logManager.append(log); Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.calOperationCostTimeFromStart(startTime); diff --git a/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java b/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java index 56fafe2f86641..e11a11eb59d43 100644 --- a/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java +++ b/server/src/main/java/org/apache/iotdb/db/writelog/node/ExclusiveWriteLogNode.java @@ -113,6 +113,9 @@ public void write(PhysicalPlan plan) throws IOException { sync(); } } catch (BufferOverflowException e) { + // if the size of a single plan bigger than logBufferWorking + // we need to clear the buffer to drop something wrong that has written. + logBufferWorking.clear(); throw new IOException("Log cannot fit into the buffer, please increase wal_buffer_size", e); } finally { lock.unlock(); diff --git a/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java b/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java index 2767813b39ad3..fd7cd0d95c4a6 100644 --- a/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java +++ b/server/src/test/java/org/apache/iotdb/db/writelog/WriteLogNodeTest.java @@ -316,6 +316,32 @@ public void testOverSizedWAL() throws IOException, IllegalPathException { } assertTrue(caught); + // if last insertplan failed by overflow,it can not take affect the next insertplan + InsertRowPlan bwInsertPlan2 = + new InsertRowPlan( + new PartialPath("root.logTestDevice.oversize"), + 100, + new String[] {"s1", "s2", "s3", "s4"}, + new TSDataType[] { + TSDataType.DOUBLE, TSDataType.INT64, TSDataType.TEXT, TSDataType.BOOLEAN + }, + // try to apply a insertplan whose size will fill the entire logBufferWorking + new String[] { + "1.0", + "15", + new String( + new char + [(IoTDBDescriptor.getInstance().getConfig().getWalBufferSize() / 2) - 109]), + "false" + }); + caught = false; + try { + logNode.write(bwInsertPlan2); + } catch (IOException e) { + caught = true; + } + assertFalse(caught); + ByteBuffer[] array = logNode.delete(); for (ByteBuffer byteBuffer : array) { MmapUtil.clean((MappedByteBuffer) byteBuffer); From 88b9dd819907006998b0d13c29d9c83f45b130ef Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 18:41:56 +0800 Subject: [PATCH 03/12] delete blank line --- .../java/org/apache/iotdb/cluster/server/member/RaftMember.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index 56dd644319588..b22ea2f073664 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -117,7 +117,6 @@ */ @SuppressWarnings("java:S3077") // reference volatile is enough public abstract class RaftMember { - private static final Logger logger = LoggerFactory.getLogger(RaftMember.class); public static final boolean USE_LOG_DISPATCHER = false; From 90393fed930f66856c49819bab95f485a6de0169 Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 16:43:33 +0800 Subject: [PATCH 04/12] [IOTDB-1583] Raft log failed to be committed in cluster version From a04a7429867950d83a71e7bca2698486ef7b8c95 Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Thu, 26 Aug 2021 14:19:40 +0800 Subject: [PATCH 05/12] use the result of serialization --- .../org/apache/iotdb/cluster/server/member/RaftMember.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index 56dd644319588..d75533bae5d0e 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -1036,7 +1036,7 @@ TSStatus processPlanLocally(PhysicalPlan plan) { // if a single log exceeds the threshold // we need to return error code to the client as in server mode - if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + if (log.serialize().capacity() + Integer.BYTES >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { logger.error( "Log cannot fit into buffer, please increase raft_log_buffer_size;" @@ -1089,7 +1089,7 @@ private TSStatus processPlanLocallyV2(PhysicalPlan plan) { startTime = Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.getOperationStartTime(); // just like processPlanLocally,we need to check the size of log - if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + if (log.serialize().capacity() + Integer.BYTES >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { logger.error( "Log cannot fit into buffer, please increase raft_log_buffer_size;" From 313b67d54d36c43dff7c2676c6508e7ac58f251c Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Thu, 26 Aug 2021 14:31:53 +0800 Subject: [PATCH 06/12] spotless apply --- .../java/org/apache/iotdb/cluster/server/member/RaftMember.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index d75533bae5d0e..8d02ba96ba18c 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -78,7 +78,6 @@ import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSStatus; -import org.apache.iotdb.tsfile.utils.RamUsageEstimator; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.thrift.TException; From 1597e24c1b7d4c807ef66d1af1eae8465e1d5746 Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 16:43:33 +0800 Subject: [PATCH 07/12] [IOTDB-1583] Raft log failed to be committed in cluster version --- .../org/apache/iotdb/cluster/server/member/RaftMember.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index 8d02ba96ba18c..56dd644319588 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -78,6 +78,7 @@ import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSStatus; +import org.apache.iotdb.tsfile.utils.RamUsageEstimator; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.thrift.TException; @@ -1035,7 +1036,7 @@ TSStatus processPlanLocally(PhysicalPlan plan) { // if a single log exceeds the threshold // we need to return error code to the client as in server mode - if (log.serialize().capacity() + Integer.BYTES + if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { logger.error( "Log cannot fit into buffer, please increase raft_log_buffer_size;" @@ -1088,7 +1089,7 @@ private TSStatus processPlanLocallyV2(PhysicalPlan plan) { startTime = Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.getOperationStartTime(); // just like processPlanLocally,we need to check the size of log - if (log.serialize().capacity() + Integer.BYTES + if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { logger.error( "Log cannot fit into buffer, please increase raft_log_buffer_size;" From 18b78921e8faba5d5e7434bd28dda246db7266a2 Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 18:41:56 +0800 Subject: [PATCH 08/12] delete blank line --- .../java/org/apache/iotdb/cluster/server/member/RaftMember.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index 56dd644319588..b22ea2f073664 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -117,7 +117,6 @@ */ @SuppressWarnings("java:S3077") // reference volatile is enough public abstract class RaftMember { - private static final Logger logger = LoggerFactory.getLogger(RaftMember.class); public static final boolean USE_LOG_DISPATCHER = false; From f2e7726f4c4b0e11a8410ee803fc5198be0a7ffa Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 25 Aug 2021 16:43:33 +0800 Subject: [PATCH 09/12] [IOTDB-1583] Raft log failed to be committed in cluster version From 75463a2464a87c0ce2bf11fa8a50b402170467db Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Thu, 26 Aug 2021 14:19:40 +0800 Subject: [PATCH 10/12] use the result of serialization --- .../org/apache/iotdb/cluster/server/member/RaftMember.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java index b22ea2f073664..c2c86729e14f4 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/server/member/RaftMember.java @@ -78,7 +78,6 @@ import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSStatus; -import org.apache.iotdb.tsfile.utils.RamUsageEstimator; import com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.thrift.TException; @@ -1035,7 +1034,7 @@ TSStatus processPlanLocally(PhysicalPlan plan) { // if a single log exceeds the threshold // we need to return error code to the client as in server mode - if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + if (log.serialize().capacity() + Integer.BYTES >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { logger.error( "Log cannot fit into buffer, please increase raft_log_buffer_size;" @@ -1088,7 +1087,7 @@ private TSStatus processPlanLocallyV2(PhysicalPlan plan) { startTime = Timer.Statistic.RAFT_SENDER_APPEND_LOG_V2.getOperationStartTime(); // just like processPlanLocally,we need to check the size of log - if ((int) RamUsageEstimator.sizeOf(log) + Integer.BYTES + if (log.serialize().capacity() + Integer.BYTES >= ClusterDescriptor.getInstance().getConfig().getRaftLogBufferSize()) { logger.error( "Log cannot fit into buffer, please increase raft_log_buffer_size;" From 586040b929a198c0355b29634e75eeec70045857 Mon Sep 17 00:00:00 2001 From: lisijia <348290933@qq.com> Date: Wed, 8 Sep 2021 15:54:11 +0800 Subject: [PATCH 11/12] [IOTDB-1619] There is an error msg when I restart iotdb-cluster --- .../log/manage/serializable/SyncLogDequeSerializer.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/serializable/SyncLogDequeSerializer.java b/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/serializable/SyncLogDequeSerializer.java index c81e6d051ee09..87f2c8bd4cb1e 100644 --- a/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/serializable/SyncLogDequeSerializer.java +++ b/cluster/src/main/java/org/apache/iotdb/cluster/log/manage/serializable/SyncLogDequeSerializer.java @@ -450,11 +450,6 @@ private void initMetaAndLogFiles() { this.firstLogIndex = meta.getCommitLogIndex() + 1; try { recoverLogFiles(); - - logDataFileList.sort(this::comparePersistLogFileName); - - logIndexFileList.sort(this::comparePersistLogFileName); - // add init log file if (logDataFileList.isEmpty()) { createNewLogFile(metaFile.getParentFile().getPath(), meta.getCommitLogIndex() + 1); @@ -473,6 +468,10 @@ private void recoverLogFiles() { // 2. recover the log data file recoverLogFiles(LOG_DATA_FILE_SUFFIX); + // sort by name before recover + logDataFileList.sort(this::comparePersistLogFileName); + logIndexFileList.sort(this::comparePersistLogFileName); + // 3. recover the last log file in case of abnormal exit recoverTheLastLogFile(); } From 9a7001e5d7274340c8d84761d3546e17ed023b9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Mar 2022 11:22:44 +0000 Subject: [PATCH 12/12] Bump jaxb-impl from 2.3.0 to 2.3.6 Bumps jaxb-impl from 2.3.0 to 2.3.6. --- updated-dependencies: - dependency-name: com.sun.xml.bind:jaxb-impl dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5396f9c7662a3..fd07c0d09e58d 100644 --- a/pom.xml +++ b/pom.xml @@ -752,7 +752,7 @@ com.sun.xml.bind jaxb-impl - 2.3.0 + 2.3.6