Skip to content

Commit

Permalink
Make Cosmos DTO objects immutable (#1013)
Browse files Browse the repository at this point in the history
  • Loading branch information
Torch3333 authored and brfrn169 committed Oct 26, 2023
1 parent 73696c3 commit 9920968
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
import com.azure.cosmos.models.CosmosQueryRequestOptions;
import com.azure.cosmos.models.PartitionKey;
import com.azure.cosmos.util.CosmosPagedIterable;
import com.google.common.collect.ImmutableList;
import com.scalar.db.config.DatabaseConfig;
import com.scalar.db.util.AdminTestUtils;
import java.util.Properties;
import org.assertj.core.util.Sets;

public class CosmosAdminTestUtils extends AdminTestUtils {

Expand Down Expand Up @@ -65,9 +65,11 @@ record ->

@Override
public void corruptMetadata(String namespace, String table) {
CosmosTableMetadata corruptedMetadata = new CosmosTableMetadata();
corruptedMetadata.setId(getFullTableName(namespace, table));
corruptedMetadata.setPartitionKeyNames(ImmutableList.of("corrupted"));
CosmosTableMetadata corruptedMetadata =
CosmosTableMetadata.newBuilder()
.id(getFullTableName(namespace, table))
.partitionKeyNames(Sets.newLinkedHashSet("corrupted"))
.build();

CosmosContainer container =
client.getDatabase(metadataDatabase).getContainer(CosmosAdmin.METADATA_CONTAINER);
Expand Down
20 changes: 10 additions & 10 deletions core/src/main/java/com/scalar/db/storage/cosmos/CosmosAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -243,24 +243,24 @@ private CosmosContainer getMetadataContainer() {

private CosmosTableMetadata convertToCosmosTableMetadata(
String fullTableName, TableMetadata tableMetadata) {
CosmosTableMetadata cosmosTableMetadata = new CosmosTableMetadata();
cosmosTableMetadata.setId(fullTableName);
cosmosTableMetadata.setPartitionKeyNames(new ArrayList<>(tableMetadata.getPartitionKeyNames()));
cosmosTableMetadata.setClusteringKeyNames(
new ArrayList<>(tableMetadata.getClusteringKeyNames()));
cosmosTableMetadata.setClusteringOrders(
Map<String, String> clusteringOrders =
tableMetadata.getClusteringKeyNames().stream()
.collect(Collectors.toMap(c -> c, c -> tableMetadata.getClusteringOrder(c).name())));
cosmosTableMetadata.setSecondaryIndexNames(tableMetadata.getSecondaryIndexNames());
.collect(Collectors.toMap(c -> c, c -> tableMetadata.getClusteringOrder(c).name()));
Map<String, String> columnTypeByName = new HashMap<>();
tableMetadata
.getColumnNames()
.forEach(
columnName ->
columnTypeByName.put(
columnName, tableMetadata.getColumnDataType(columnName).name().toLowerCase()));
cosmosTableMetadata.setColumns(columnTypeByName);
return cosmosTableMetadata;
return CosmosTableMetadata.newBuilder()
.id(fullTableName)
.partitionKeyNames(tableMetadata.getPartitionKeyNames())
.clusteringKeyNames(tableMetadata.getClusteringKeyNames())
.clusteringOrders(clusteringOrders)
.secondaryIndexNames(tableMetadata.getSecondaryIndexNames())
.columns(columnTypeByName)
.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.scalar.db.api.TableMetadata;
import com.scalar.db.io.Column;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
Expand Down Expand Up @@ -53,20 +54,18 @@ public MutationType getMutationType() {
@Nonnull
public Record makeRecord() {
Mutation mutation = (Mutation) getOperation();
Record record = new Record();

if (mutation instanceof Delete) {
return record;
return new Record();
}
Put put = (Put) mutation;

record.setId(getId());
record.setConcatenatedPartitionKey(getConcatenatedPartitionKey());
record.setPartitionKey(toMap(put.getPartitionKey().getColumns()));
put.getClusteringKey().ifPresent(k -> record.setClusteringKey(toMap(k.getColumns())));
record.setValues(toMapForPut(put));

return record;
return new Record(
getId(),
getConcatenatedPartitionKey(),
toMap(put.getPartitionKey().getColumns()),
put.getClusteringKey().map(k -> toMap(k.getColumns())).orElse(Collections.emptyMap()),
toMapForPut(put));
}

@Nonnull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,59 @@

import com.google.common.base.MoreObjects;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/**
* A metadata class for a table of ScalarDB to know the type of each column
*
* @author Yuji Ito
*/
@SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
@NotThreadSafe
@Immutable
public class CosmosTableMetadata {
private String id;
private LinkedHashSet<String> partitionKeyNames;
private LinkedHashSet<String> clusteringKeyNames;
private Map<String, String> clusteringOrders;
private Set<String> secondaryIndexNames;
private Map<String, String> columns;

public CosmosTableMetadata() {}

public void setId(String id) {
this.id = id;
}

public void setPartitionKeyNames(List<String> partitionKeyNames) {
this.partitionKeyNames = new LinkedHashSet<>(partitionKeyNames);
}

public void setClusteringKeyNames(List<String> clusteringKeyNames) {
this.clusteringKeyNames = new LinkedHashSet<>(clusteringKeyNames);
}

public void setClusteringOrders(Map<String, String> clusteringOrders) {
this.clusteringOrders = clusteringOrders;
private final String id;
private final LinkedHashSet<String> partitionKeyNames;
private final LinkedHashSet<String> clusteringKeyNames;
private final Map<String, String> clusteringOrders;
private final Set<String> secondaryIndexNames;
private final Map<String, String> columns;
// The default constructor is required by the Cosmos SDK which uses Jackson to deserialize JSON
// object
public CosmosTableMetadata() {
this(null, null, null, null, null, null);
}

public void setSecondaryIndexNames(Set<String> secondaryIndexNames) {
this.secondaryIndexNames = secondaryIndexNames;
public CosmosTableMetadata(
@Nullable String id,
@Nullable LinkedHashSet<String> partitionKeyNames,
@Nullable LinkedHashSet<String> clusteringKeyNames,
@Nullable Map<String, String> clusteringOrders,
@Nullable Set<String> secondaryIndexNames,
@Nullable Map<String, String> columns) {
this.id = id != null ? id : "";
this.partitionKeyNames = partitionKeyNames != null ? partitionKeyNames : new LinkedHashSet<>();
this.clusteringKeyNames =
clusteringKeyNames != null ? clusteringKeyNames : new LinkedHashSet<>();
this.clusteringOrders = clusteringOrders != null ? clusteringOrders : Collections.emptyMap();
this.secondaryIndexNames =
secondaryIndexNames != null ? secondaryIndexNames : Collections.emptySet();
this.columns = columns != null ? columns : Collections.emptyMap();
}

public void setColumns(Map<String, String> columns) {
this.columns = columns;
private CosmosTableMetadata(Builder builder) {
this(
builder.id,
builder.partitionKeyNames,
builder.clusteringKeyNames,
builder.clusteringOrders,
builder.secondaryIndexNames,
builder.columns);
}

public String getId() {
Expand Down Expand Up @@ -108,4 +115,54 @@ public String toString() {
.add("columns", columns)
.toString();
}

public static Builder newBuilder() {
return new Builder();
}

public static final class Builder {

private String id;
private LinkedHashSet<String> partitionKeyNames;
private LinkedHashSet<String> clusteringKeyNames;
private Map<String, String> clusteringOrders;
private Set<String> secondaryIndexNames;
private Map<String, String> columns;

private Builder() {}

public Builder id(String val) {
id = val;
return this;
}

public Builder partitionKeyNames(LinkedHashSet<String> val) {
partitionKeyNames = val;
return this;
}

public Builder clusteringKeyNames(LinkedHashSet<String> val) {
clusteringKeyNames = val;
return this;
}

public Builder clusteringOrders(Map<String, String> val) {
clusteringOrders = val;
return this;
}

public Builder secondaryIndexNames(Set<String> val) {
secondaryIndexNames = val;
return this;
}

public Builder columns(Map<String, String> val) {
columns = val;
return this;
}

public CosmosTableMetadata build() {
return new CosmosTableMetadata(this);
}
}
}
51 changes: 25 additions & 26 deletions core/src/main/java/com/scalar/db/storage/cosmos/Record.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,41 @@
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

/**
* A record class that Cosmos DB uses for storing a document based on ScalarDB data model.
*
* @author Yuji Ito
*/
@SuppressFBWarnings({"EI_EXPOSE_REP", "EI_EXPOSE_REP2"})
@NotThreadSafe
@Immutable
public class Record {
private String id = "";
private String concatenatedPartitionKey = "";
private Map<String, Object> partitionKey = Collections.emptyMap();
private Map<String, Object> clusteringKey = Collections.emptyMap();
private Map<String, Object> values = Collections.emptyMap();

public Record() {}

public void setId(String id) {
this.id = id;
}

public void setConcatenatedPartitionKey(String concatenatedPartitionKey) {
this.concatenatedPartitionKey = concatenatedPartitionKey;
}

public void setPartitionKey(Map<String, Object> partitionKey) {
this.partitionKey = partitionKey;
}

public void setClusteringKey(Map<String, Object> clusteringKey) {
this.clusteringKey = clusteringKey;
private final String id;
private final String concatenatedPartitionKey;
private final Map<String, Object> partitionKey;
private final Map<String, Object> clusteringKey;
private final Map<String, Object> values;

// The default constructor is required by the Cosmos SDK which uses Jackson to deserialize JSON
// object
public Record() {
this(null, null, null, null, null);
}

public void setValues(Map<String, Object> values) {
this.values = values;
public Record(
@Nullable String id,
@Nullable String concatenatedPartitionKey,
@Nullable Map<String, Object> partitionKey,
@Nullable Map<String, Object> clusteringKey,
@Nullable Map<String, Object> values) {
this.id = id != null ? id : "";
this.concatenatedPartitionKey =
concatenatedPartitionKey != null ? concatenatedPartitionKey : "";
this.partitionKey = partitionKey != null ? partitionKey : Collections.emptyMap();
this.clusteringKey = clusteringKey != null ? clusteringKey : Collections.emptyMap();
this.values = values != null ? values : Collections.emptyMap();
}

public String getId() {
Expand Down
Loading

0 comments on commit 9920968

Please sign in to comment.