blob: b43410d44c4fb795ea4f7c4d4c8d90d70ba2ba91 [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.hadoop.hive.metastore;
import org.apache.hadoop.hive.metastore.api.LockComponent;
import org.apache.hadoop.hive.metastore.api.LockRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Builder class to make constructing {@link LockRequest} easier.
*/
public class LockRequestBuilder {
private LockRequest req;
private LockTrie trie;
private boolean userSet;
/**
* @deprecated
*/
public LockRequestBuilder() {
this(null);
}
public LockRequestBuilder(String agentInfo) {
req = new LockRequest();
trie = new LockTrie();
userSet = false;
if(agentInfo != null) {
req.setAgentInfo(agentInfo);
}
}
/**
* Get the constructed LockRequest.
* @return lock request
*/
public LockRequest build() {
if (!userSet) {
throw new RuntimeException("Cannot build a lock without giving a user");
}
trie.addLocksToRequest(req);
try {
req.setHostname(InetAddress.getLocalHost().getHostName());
} catch (UnknownHostException e) {
throw new RuntimeException("Unable to determine our local host!");
}
return req;
}
/**
* Set the transaction id.
* @param txnid transaction id
* @return reference to this builder
*/
public LockRequestBuilder setTransactionId(long txnid) {
req.setTxnid(txnid);
return this;
}
public LockRequestBuilder setUser(String user) {
if (user == null) user = "unknown";
req.setUser(user);
userSet = true;
return this;
}
public LockRequestBuilder setZeroWaitReadEnabled(boolean zeroWaitRead) {
req.setZeroWaitReadEnabled(zeroWaitRead);
return this;
}
/**
* Add a lock component to the lock request
* @param component to add
* @return reference to this builder
*/
public LockRequestBuilder addLockComponent(LockComponent component) {
trie.add(component);
return this;
}
/**
* Add a collection with lock components to the lock request
* @param components to add
* @return reference to this builder
*/
public LockRequestBuilder addLockComponents(Collection<LockComponent> components) {
trie.addAll(components);
return this;
}
// For reasons that are completely incomprehensible to me the semantic
// analyzers often ask for multiple locks on the same entity (for example
// a shared_read and an exlcusive lock). The db locking system gets confused
// by this and dead locks on it. To resolve that, we'll make sure in the
// request that multiple locks are coalesced and promoted to the higher
// level of locking. To do this we put all locks components in trie based
// on dbname, tablename, partition name and handle the promotion as new
// requests come in. This structure depends on the fact that null is a
// valid key in a LinkedHashMap. So a database lock will map to (dbname, null,
// null).
private static class LockTrie {
private LockTypeComparator lockTypeComparator = new LockTypeComparator();
private Map<String, TableTrie> trie;
LockTrie() {
trie = new LinkedHashMap<>();
}
public void add(LockComponent comp) {
TableTrie tabs = trie.get(comp.getDbname());
if (tabs == null) {
tabs = new TableTrie();
trie.put(comp.getDbname(), tabs);
}
setTable(comp, tabs);
}
public void addAll(Collection<LockComponent> components) {
for(LockComponent component: components) {
add(component);
}
}
public void addLocksToRequest(LockRequest request) {
for (TableTrie tab : trie.values()) {
for (PartTrie part : tab.values()) {
for (LockComponent lock : part.values()) {
request.addToComponent(lock);
}
}
}
}
private void setTable(LockComponent comp, TableTrie tabs) {
PartTrie parts = tabs.get(comp.getTablename());
if (parts == null) {
parts = new PartTrie();
tabs.put(comp.getTablename(), parts);
}
setPart(comp, parts);
}
private void setPart(LockComponent comp, PartTrie parts) {
LockComponent existing = parts.get(comp.getPartitionname());
if (existing == null) {
// No existing lock for this partition.
parts.put(comp.getPartitionname(), comp);
} else if (lockTypeComparator.compare(comp.getType(), existing.getType()) > 0) {
// We only need to promote if comp.type is > existing.type.
parts.put(comp.getPartitionname(), comp);
}
}
private static class TableTrie extends LinkedHashMap<String, PartTrie> {
}
private static class PartTrie extends LinkedHashMap<String, LockComponent> {
}
}
}