001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.quotas; 019 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.Map; 023import java.util.Set; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.TableName; 026import org.apache.yetus.audience.InterfaceAudience; 027import org.apache.yetus.audience.InterfaceStability; 028 029import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; 030 031/** 032 * In-Memory state of the user quotas 033 */ 034@InterfaceAudience.Private 035@InterfaceStability.Evolving 036@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "IS2_INCONSISTENT_SYNC", 037 justification = "FindBugs seems confused; says bypassGlobals, namepaceLimiters, and " 038 + "tableLimiters are mostly synchronized..." 039 + "but to me it looks like they are totally synchronized") 040public class UserQuotaState extends QuotaState { 041 private Map<String, QuotaLimiter> namespaceLimiters = null; 042 private Map<TableName, QuotaLimiter> tableLimiters = null; 043 private boolean bypassGlobals = false; 044 045 @Override 046 public synchronized String toString() { 047 StringBuilder builder = new StringBuilder(); 048 builder.append("UserQuotaState("); 049 if (bypassGlobals) { 050 builder.append("bypass-globals"); 051 } 052 053 if (isBypass()) { 054 builder.append(" bypass"); 055 } else { 056 if (getGlobalLimiter() != NoopQuotaLimiter.get()) { 057 builder.append(" global-limiter"); 058 } 059 060 if (tableLimiters != null && !tableLimiters.isEmpty()) { 061 builder.append(" ["); 062 for (TableName table : tableLimiters.keySet()) { 063 builder.append(" " + table); 064 } 065 builder.append(" ]"); 066 } 067 068 if (namespaceLimiters != null && !namespaceLimiters.isEmpty()) { 069 builder.append(" ["); 070 for (String ns : namespaceLimiters.keySet()) { 071 builder.append(" " + ns); 072 } 073 builder.append(" ]"); 074 } 075 } 076 builder.append(')'); 077 return builder.toString(); 078 } 079 080 /** Returns true if there is no quota information associated to this object */ 081 @Override 082 public synchronized boolean isBypass() { 083 return !bypassGlobals && getGlobalLimiter() == NoopQuotaLimiter.get() 084 && (tableLimiters == null || tableLimiters.isEmpty()) 085 && (namespaceLimiters == null || namespaceLimiters.isEmpty()); 086 } 087 088 public synchronized boolean hasBypassGlobals() { 089 return bypassGlobals; 090 } 091 092 @Override 093 public synchronized void setQuotas(Configuration conf, final Quotas quotas) { 094 super.setQuotas(conf, quotas); 095 bypassGlobals = quotas.getBypassGlobals(); 096 } 097 098 /** 099 * Add the quota information of the specified table. (This operation is part of the QuotaState 100 * setup) 101 */ 102 public synchronized void setQuotas(Configuration conf, final TableName table, Quotas quotas) { 103 tableLimiters = setLimiter(conf, tableLimiters, table, quotas); 104 } 105 106 /** 107 * Add the quota information of the specified namespace. (This operation is part of the QuotaState 108 * setup) 109 */ 110 public void setQuotas(Configuration conf, final String namespace, Quotas quotas) { 111 namespaceLimiters = setLimiter(conf, namespaceLimiters, namespace, quotas); 112 } 113 114 public boolean hasTableLimiters() { 115 return tableLimiters != null && !tableLimiters.isEmpty(); 116 } 117 118 private <K> Map<K, QuotaLimiter> setLimiter(Configuration conf, Map<K, QuotaLimiter> limiters, 119 final K key, final Quotas quotas) { 120 if (limiters == null) { 121 limiters = new HashMap<>(); 122 } 123 124 QuotaLimiter limiter = 125 quotas.hasThrottle() ? QuotaLimiterFactory.fromThrottle(conf, quotas.getThrottle()) : null; 126 if (limiter != null && !limiter.isBypass()) { 127 limiters.put(key, limiter); 128 } else { 129 limiters.remove(key); 130 } 131 return limiters; 132 } 133 134 /** 135 * Perform an update of the quota state based on the other quota state object. (This operation is 136 * executed by the QuotaCache) 137 */ 138 @Override 139 public synchronized void update(final QuotaState other) { 140 super.update(other); 141 142 if (other instanceof UserQuotaState) { 143 UserQuotaState uOther = (UserQuotaState) other; 144 tableLimiters = updateLimiters(tableLimiters, uOther.tableLimiters); 145 namespaceLimiters = updateLimiters(namespaceLimiters, uOther.namespaceLimiters); 146 bypassGlobals = uOther.bypassGlobals; 147 } else { 148 tableLimiters = null; 149 namespaceLimiters = null; 150 bypassGlobals = false; 151 } 152 } 153 154 private static <K> Map<K, QuotaLimiter> updateLimiters(final Map<K, QuotaLimiter> map, 155 final Map<K, QuotaLimiter> otherMap) { 156 if (map == null) { 157 return otherMap; 158 } 159 160 if (otherMap != null) { 161 // To Remove 162 Set<K> toRemove = new HashSet<>(map.keySet()); 163 toRemove.removeAll(otherMap.keySet()); 164 map.keySet().removeAll(toRemove); 165 166 // To Update/Add 167 for (final Map.Entry<K, QuotaLimiter> entry : otherMap.entrySet()) { 168 QuotaLimiter limiter = map.get(entry.getKey()); 169 if (limiter == null) { 170 limiter = entry.getValue(); 171 } else { 172 limiter = QuotaLimiterFactory.update(limiter, entry.getValue()); 173 } 174 map.put(entry.getKey(), limiter); 175 } 176 return map; 177 } 178 return null; 179 } 180 181 /** 182 * Return the limiter for the specified table associated with this quota. If the table does not 183 * have its own quota limiter the global one will be returned. In case there is no quota limiter 184 * associated with this object a noop limiter will be returned. 185 * @return the quota limiter for the specified table 186 */ 187 public synchronized QuotaLimiter getTableLimiter(final TableName table) { 188 if (tableLimiters != null) { 189 QuotaLimiter limiter = tableLimiters.get(table); 190 if (limiter != null) return limiter; 191 } 192 if (namespaceLimiters != null) { 193 QuotaLimiter limiter = namespaceLimiters.get(table.getNamespaceAsString()); 194 if (limiter != null) return limiter; 195 } 196 return getGlobalLimiter(); 197 } 198}