blob: d947d1d690e4e9dc72cacf2630ffe47c7447585a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.schema;
import java.util.Collections;
import java.util.Map;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import org.apache.cassandra.utils.Pair;
/**
* Manages the cached {@link TableMetadataRef} objects which holds the references to {@link TableMetadata} objects.
* <p>
* The purpose of {@link TableMetadataRef} is that the reference to {@link TableMetadataRef} remains unchanged when
* the metadata of the table changes. {@link TableMetadata} is immutable, so when it changes, we only switch
* the reference inside the existing {@link TableMetadataRef} object.
*/
class TableMetadataRefCache
{
public final static TableMetadataRefCache EMPTY = new TableMetadataRefCache(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());
// UUID -> mutable metadata ref map. We have to update these in place every time a table changes.
private final Map<TableId, TableMetadataRef> metadataRefs;
// keyspace and table names -> mutable metadata ref map.
private final Map<Pair<String, String>, TableMetadataRef> metadataRefsByName;
// (keyspace name, index name) -> mutable metadata ref map. We have to update these in place every time an index changes.
private final Map<Pair<String, String>, TableMetadataRef> indexMetadataRefs;
public TableMetadataRefCache(Map<TableId, TableMetadataRef> metadataRefs,
Map<Pair<String, String>, TableMetadataRef> metadataRefsByName,
Map<Pair<String, String>, TableMetadataRef> indexMetadataRefs)
{
this.metadataRefs = Collections.unmodifiableMap(metadataRefs);
this.metadataRefsByName = Collections.unmodifiableMap(metadataRefsByName);
this.indexMetadataRefs = Collections.unmodifiableMap(indexMetadataRefs);
}
/**
* Returns cache copy with added the {@link TableMetadataRef} corresponding to the provided keyspace to {@link #metadataRefs} and
* {@link #indexMetadataRefs}, assuming the keyspace is new (in the sense of not being tracked by the manager yet).
*/
TableMetadataRefCache withNewRefs(KeyspaceMetadata ksm)
{
return withUpdatedRefs(ksm.empty(), ksm);
}
/**
* Returns cache copy with updated the {@link TableMetadataRef} in {@link #metadataRefs} and {@link #indexMetadataRefs},
* for an existing updated keyspace given it's previous and new definition.
* <p>
* Note that {@link TableMetadataRef} are not duplicated and table metadata is altered in the existing refs.
*/
TableMetadataRefCache withUpdatedRefs(KeyspaceMetadata previous, KeyspaceMetadata updated)
{
Tables.TablesDiff tablesDiff = Tables.diff(previous.tables, updated.tables);
Views.ViewsDiff viewsDiff = Views.diff(previous.views, updated.views);
MapDifference<String, TableMetadata> indexesDiff = previous.tables.indexesDiff(updated.tables);
boolean hasCreatedOrDroppedTablesOrViews = tablesDiff.created.size() > 0 || tablesDiff.dropped.size() > 0 || viewsDiff.created.size() > 0 || viewsDiff.dropped.size() > 0;
boolean hasCreatedOrDroppedIndexes = !indexesDiff.entriesOnlyOnRight().isEmpty() || !indexesDiff.entriesOnlyOnLeft().isEmpty();
Map<TableId, TableMetadataRef> metadataRefs = hasCreatedOrDroppedTablesOrViews ? Maps.newHashMap(this.metadataRefs) : this.metadataRefs;
Map<Pair<String, String>, TableMetadataRef> metadataRefsByName = hasCreatedOrDroppedTablesOrViews ? Maps.newHashMap(this.metadataRefsByName) : this.metadataRefsByName;
Map<Pair<String, String>, TableMetadataRef> indexMetadataRefs = hasCreatedOrDroppedIndexes ? Maps.newHashMap(this.indexMetadataRefs) : this.indexMetadataRefs;
// clean up after removed entries
tablesDiff.dropped.forEach(ref -> removeRef(metadataRefs, metadataRefsByName, ref));
viewsDiff.dropped.forEach(view -> removeRef(metadataRefs, metadataRefsByName, view.metadata));
indexesDiff.entriesOnlyOnLeft()
.values()
.forEach(indexTable -> indexMetadataRefs.remove(Pair.create(indexTable.keyspace, indexTable.indexName().get())));
// load up new entries
tablesDiff.created.forEach(table -> putRef(metadataRefs, metadataRefsByName, new TableMetadataRef(table)));
viewsDiff.created.forEach(view -> putRef(metadataRefs, metadataRefsByName, new TableMetadataRef(view.metadata)));
indexesDiff.entriesOnlyOnRight()
.values()
.forEach(indexTable -> indexMetadataRefs.put(Pair.create(indexTable.keyspace, indexTable.indexName().get()), new TableMetadataRef(indexTable)));
// refresh refs to updated ones
tablesDiff.altered.forEach(diff -> metadataRefs.get(diff.after.id).set(diff.after));
viewsDiff.altered.forEach(diff -> metadataRefs.get(diff.after.metadata.id).set(diff.after.metadata));
indexesDiff.entriesDiffering()
.values()
.stream()
.map(MapDifference.ValueDifference::rightValue)
.forEach(indexTable -> indexMetadataRefs.get(Pair.create(indexTable.keyspace, indexTable.indexName().get())).set(indexTable));
return new TableMetadataRefCache(metadataRefs, metadataRefsByName, indexMetadataRefs);
}
private void putRef(Map<TableId, TableMetadataRef> metadataRefs,
Map<Pair<String, String>, TableMetadataRef> metadataRefsByName,
TableMetadataRef ref)
{
metadataRefs.put(ref.id, ref);
metadataRefsByName.put(Pair.create(ref.keyspace, ref.name), ref);
}
private void removeRef(Map<TableId, TableMetadataRef> metadataRefs,
Map<Pair<String, String>, TableMetadataRef> metadataRefsByName,
TableMetadata tm)
{
metadataRefs.remove(tm.id);
metadataRefsByName.remove(Pair.create(tm.keyspace, tm.name));
}
/**
* Returns cache copy with removed the {@link TableMetadataRef} from {@link #metadataRefs} and {@link #indexMetadataRefs}
* for the provided (dropped) keyspace.
*/
TableMetadataRefCache withRemovedRefs(KeyspaceMetadata ksm)
{
return withUpdatedRefs(ksm, ksm.empty());
}
public TableMetadataRef getTableMetadataRef(TableId id)
{
return metadataRefs.get(id);
}
public TableMetadataRef getTableMetadataRef(String keyspace, String table)
{
return metadataRefsByName.get(Pair.create(keyspace, table));
}
public TableMetadataRef getIndexTableMetadataRef(String keyspace, String index)
{
return indexMetadataRefs.get(Pair.create(keyspace, index));
}
}