Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.apache.iotdb.db.schemaengine.metric.SchemaRegionMemMetric;
import org.apache.iotdb.db.schemaengine.rescon.MemSchemaRegionStatistics;
import org.apache.iotdb.db.schemaengine.schemaregion.mtree.impl.mem.mnode.IMemMNode;
import org.apache.iotdb.db.schemaengine.schemaregion.mtree.impl.mem.mnode.impl.BasicInternalMNode;
import org.apache.iotdb.db.schemaengine.schemaregion.mtree.loader.MNodeFactoryLoader;
import org.apache.iotdb.db.schemaengine.schemaregion.mtree.traverser.collector.EntityCollector;
import org.apache.iotdb.db.schemaengine.schemaregion.mtree.traverser.collector.MNodeCollector;
Expand Down Expand Up @@ -374,6 +375,7 @@ public List<IMeasurementMNode<IMemMNode>> createAlignedTimeSeries(
throws MetadataException {
final List<IMeasurementMNode<IMemMNode>> measurementMNodeList = new ArrayList<>();
MetaFormatUtils.checkSchemaMeasurementNames(measurements);
MetaFormatUtils.checkSchemaMeasurementAliases(devicePath, measurements, aliasList);
final IMemMNode deviceParent = checkAndAutoCreateInternalPath(devicePath);

// synchronize check and add, we need addChild operation be atomic.
Expand Down Expand Up @@ -607,28 +609,13 @@ public IMeasurementMNode<IMemMNode> deleteTimeseries(PartialPath path) throws Me
public void deleteEmptyInternalMNode(IDeviceMNode<IMemMNode> entityMNode) {
IMemMNode curNode = entityMNode.getAsMNode();
if (!entityMNode.isUseTemplate()) {
boolean hasMeasurement = false;
boolean hasNonViewMeasurement = false;
IMemMNode child;
try (final IMNodeIterator<IMemMNode> iterator = store.getChildrenIterator(curNode)) {
while (iterator.hasNext()) {
child = iterator.next();
if (child.isMeasurement()) {
hasMeasurement = true;
if (!child.getAsMeasurementMNode().isLogicalView()) {
hasNonViewMeasurement = true;
break;
}
}
}
}

if (!hasMeasurement) {
final BasicInternalMNode internalNode = (BasicInternalMNode) curNode.getBasicMNode();
if (internalNode.getMeasurementChildCount() == 0) {
synchronized (this) {
curNode = store.setToInternal(entityMNode);
}
refreshAncestorsHavingDeviceDescendant(curNode.getParent());
} else if (!hasNonViewMeasurement) {
} else if (internalNode.getNonViewMeasurementChildCount() == 0) {
// has some measurement but they are all logical view
entityMNode.setAligned(null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public class BasicInternalMNode extends BasicMNode implements IInternalMNode<IMe
@SuppressWarnings("squid:S3077")
private volatile IDeviceInfo<IMemMNode> deviceInfo = null;

// Direct children only. Snapshot loading rebuilds these counters through addChild.
private int measurementChildCount;
private int nonViewMeasurementChildCount;

/** Constructor of MNode. */
public BasicInternalMNode(IMemMNode parent, String name) {
super(parent, name);
Expand Down Expand Up @@ -97,6 +101,9 @@ public IMemMNode addChild(String name, IMemMNode child) {
}
child.setParent(this);
IMemMNode existingChild = children.putIfAbsent(name, child);
if (existingChild == null) {
updateMeasurementChildCount(child, 1);
}
return existingChild == null ? child : existingChild;
}

Expand All @@ -113,29 +120,19 @@ public IMemMNode addChild(String name, IMemMNode child) {
*/
@Override
public IMemMNode addChild(IMemMNode child) {
/* use cpu time to exchange memory
* measurementNode's children should be null to save memory
* add child method will only be called when writing MTree, which is not a frequent operation
*/
if (children == null) {
// double check, children is volatile
synchronized (this) {
if (children == null) {
children = new MemMNodeContainer();
}
}
}

child.setParent(this);
children.putIfAbsent(child.getName(), child);
addChild(child.getName(), child);
return child;
}

/** Delete a child. */
@Override
public IMemMNode deleteChild(String name) {
if (children != null) {
return children.remove(name);
final IMemMNode child = children.remove(name);
if (child != null) {
updateMeasurementChildCount(child, -1);
}
return child;
}
return null;
}
Expand All @@ -151,12 +148,39 @@ public IMNodeContainer<IMemMNode> getChildren() {
@Override
public void setChildren(IMNodeContainer<IMemMNode> children) {
this.children = children;
measurementChildCount = 0;
nonViewMeasurementChildCount = 0;
if (children != null) {
children.values().forEach(child -> updateMeasurementChildCount(child, 1));
}
}

private void updateMeasurementChildCount(final IMemMNode child, final int delta) {
if (child.isMeasurement()) {
measurementChildCount += delta;
if (!child.getAsMeasurementMNode().isLogicalView()) {
nonViewMeasurementChildCount += delta;
}
}
}

public int getMeasurementChildCount() {
return measurementChildCount;
}

public int getNonViewMeasurementChildCount() {
return nonViewMeasurementChildCount;
}

/** MNodeContainer reference and basic occupation, 8 + 80B. DeviceInfo reference and size. */
/** MNodeContainer reference and basic occupation, 8 + 80B. Counters, 8B. DeviceInfo and size. */
@Override
public int estimateSize() {
return 8 + 80 + super.estimateSize() + 8 + (deviceInfo == null ? 0 : deviceInfo.estimateSize());
return 8
+ 80
+ 8
+ super.estimateSize()
+ 8
+ (deviceInfo == null ? 0 : deviceInfo.estimateSize());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,7 @@ public List<IMeasurementMNode<ICachedMNode>> createAlignedTimeSeries(
throws MetadataException {
final List<IMeasurementMNode<ICachedMNode>> measurementMNodeList = new ArrayList<>();
MetaFormatUtils.checkSchemaMeasurementNames(measurements);
MetaFormatUtils.checkSchemaMeasurementAliases(devicePath, measurements, aliasList);
final ICachedMNode deviceParent = checkAndAutoCreateInternalPath(devicePath);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@
import org.apache.iotdb.commons.exception.MetadataException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.db.conf.IoTDBConfig;
import org.apache.iotdb.db.exception.metadata.AliasAlreadyExistException;
import org.apache.iotdb.db.exception.metadata.IllegalParameterOfPathException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import static org.apache.iotdb.commons.conf.IoTDBConstant.LOSS;
import static org.apache.iotdb.commons.conf.IoTDBConstant.SDT;
Expand Down Expand Up @@ -101,6 +104,26 @@ public static void checkSchemaMeasurementNames(List<String> measurements)
}
}

/** Validate the shared measurement/alias namespace before creating any member of a batch. */
public static void checkSchemaMeasurementAliases(
final PartialPath devicePath, final List<String> measurements, final List<String> aliases)
throws MetadataException {
if (aliases == null) {
return;
}
final Set<String> measurementNames = new HashSet<>(measurements);
final Set<String> aliasNames = new HashSet<>();
for (int i = 0; i < measurements.size(); i++) {
final String alias = aliases.get(i);
if (alias != null
&& (!aliasNames.add(alias)
|| (measurementNames.contains(alias) && !alias.equals(measurements.get(i))))) {
throw new AliasAlreadyExistException(
devicePath.concatAsMeasurementPath(measurements.get(i)).getFullPath(), alias);
}
}
}

/** check whether the database name uses illegal characters */
public static void checkDatabase(String database) throws IllegalPathException {
if (!IoTDBConfig.STORAGE_GROUP_PATTERN.matcher(database).matches()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
import org.apache.iotdb.commons.exception.MetadataException;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
import org.apache.iotdb.db.exception.metadata.AliasAlreadyExistException;
import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion;
import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ITimeSeriesSchemaInfo;
import org.apache.iotdb.db.schemaengine.schemaregion.write.req.impl.CreateAlignedTimeSeriesPlanImpl;

import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.file.metadata.enums.CompressionType;
Expand All @@ -34,6 +36,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -58,6 +61,65 @@ public void setUp() throws Exception {
schemaRegion = getSchemaRegion("root.sg", 0);
}

@Test
public void testAlignedBatchRejectsConflictingAliasesBeforeCreation() throws Exception {
for (final List<String> aliases :
Arrays.asList(
Arrays.asList("dup", "dup"), Arrays.asList("s2", null), Arrays.asList(null, "s1"))) {
final CreateAlignedTimeSeriesPlanImpl plan = alignedAliasPlan(aliases);
try {
schemaRegion.createAlignedTimeSeries(plan);
Assert.fail("Expected alias collision");
} catch (final AliasAlreadyExistException expected) {
Assert.assertTrue(
SchemaRegionTestUtil.showTimeseries(schemaRegion, new PartialPath("root.sg.batch.*"))
.isEmpty());
}
}
schemaRegion.createAlignedTimeSeries(alignedAliasPlan(Arrays.asList("a1", "a2")));
final CreateAlignedTimeSeriesPlanImpl merge = alignedAliasPlan(Arrays.asList("dup", "dup"));
merge.setWithMerge(true);
try {
schemaRegion.createAlignedTimeSeries(merge);
Assert.fail("Expected alias collision during merge");
} catch (final AliasAlreadyExistException expected) {
checkAliasAndTagsAndAttributes(
"root.sg.batch.s1", "a1", Collections.emptyMap(), Collections.emptyMap());
checkAliasAndTagsAndAttributes(
"root.sg.batch.s2", "a2", Collections.emptyMap(), Collections.emptyMap());
}
}

@Test
public void testAlignedSelfAliasAndRemainingAliasAfterDeletion() throws Exception {
schemaRegion.createAlignedTimeSeries(alignedAliasPlan(Arrays.asList("s1", "a2")));
final PathPatternTree delete = new PathPatternTree();
delete.appendPathPattern(new PartialPath("root.sg.batch.s2"));
delete.constructTree();
schemaRegion.constructSchemaBlackList(delete);
schemaRegion.deleteTimeseriesInBlackList(delete);
checkAliasAndTagsAndAttributes(
"root.sg.batch.s1", "s1", Collections.emptyMap(), Collections.emptyMap());
Assert.assertEquals(
"root.sg.batch.s1",
SchemaRegionTestUtil.showTimeseries(schemaRegion, new PartialPath("root.sg.batch.s1"))
.get(0)
.getFullPath());
}

private CreateAlignedTimeSeriesPlanImpl alignedAliasPlan(final List<String> aliases)
throws MetadataException {
return new CreateAlignedTimeSeriesPlanImpl(
new PartialPath("root.sg.batch"),
new ArrayList<>(Arrays.asList("s1", "s2")),
new ArrayList<>(Collections.nCopies(2, TSDataType.INT32)),
new ArrayList<>(Collections.nCopies(2, TSEncoding.PLAIN)),
new ArrayList<>(Collections.nCopies(2, CompressionType.SNAPPY)),
new ArrayList<>(aliases),
null,
null);
}

/**
* Prepare timeseries
*
Expand Down
Loading
Loading