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.hbase.TableName; 025import org.apache.yetus.audience.InterfaceAudience; 026import org.apache.yetus.audience.InterfaceStability; 027 028import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas; 029 030/** 031 * In-Memory state of the user quotas 032 */ 033@InterfaceAudience.Private 034@InterfaceStability.Evolving 035@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "IS2_INCONSISTENT_SYNC", 036 justification = "FindBugs seems confused; says bypassGlobals, namepaceLimiters, and " 037 + "tableLimiters are mostly synchronized..." 038 + "but to me it looks like they are totally synchronized") 039public class UserQuotaState extends QuotaState { 040 private Map<String, QuotaLimiter> namespaceLimiters = null; 041 private Map<TableName, QuotaLimiter> tableLimiters = null; 042 private boolean bypassGlobals = false; 043 044 @Override 045 public synchronized String toString() { 046 StringBuilder builder = new StringBuilder(); 047 builder.append("UserQuotaState("); 048 if (bypassGlobals) { 049 builder.append("bypass-globals"); 050 } 051 052 if (isBypass()) { 053 builder.append(" bypass"); 054 } else { 055 if (getGlobalLimiter() != NoopQuotaLimiter.get()) { 056 builder.append(" global-limiter"); 057 } 058 059 if (tableLimiters != null && !tableLimiters.isEmpty()) { 060 builder.append(" ["); 061 for (TableName table : tableLimiters.keySet()) { 062 builder.append(" " + table); 063 } 064 builder.append(" ]"); 065 } 066 067 if (namespaceLimiters != null && !namespaceLimiters.isEmpty()) { 068 builder.append(" ["); 069 for (String ns : namespaceLimiters.keySet()) { 070 builder.append(" " + ns); 071 } 072 builder.append(" ]"); 073 } 074 } 075 builder.append(')'); 076 return builder.toString(); 077 } 078 079 /** Returns true if there is no quota information associated to this object */ 080 @Override 081 public synchronized boolean isBypass() { 082 return !bypassGlobals && getGlobalLimiter() == NoopQuotaLimiter.get() 083 && (tableLimiters == null || tableLimiters.isEmpty()) 084 && (namespaceLimiters == null || namespaceLimiters.isEmpty()); 085 } 086 087 public synchronized boolean hasBypassGlobals() { 088 return bypassGlobals; 089 } 090 091 @Override 092 public synchronized void setQuotas(final Quotas quotas) { 093 super.setQuotas(quotas); 094 bypassGlobals = quotas.getBypassGlobals(); 095 } 096 097 /** 098 * Add the quota information of the specified table. (This operation is part of the QuotaState 099 * setup) 100 */ 101 public synchronized void setQuotas(final TableName table, Quotas quotas) { 102 tableLimiters = setLimiter(tableLimiters, table, quotas); 103 } 104 105 /** 106 * Add the quota information of the specified namespace. (This operation is part of the QuotaState 107 * setup) 108 */ 109 public void setQuotas(final String namespace, Quotas quotas) { 110 namespaceLimiters = setLimiter(namespaceLimiters, namespace, quotas); 111 } 112 113 public boolean hasTableLimiters() { 114 return tableLimiters != null && !tableLimiters.isEmpty(); 115 } 116 117 private <K> Map<K, QuotaLimiter> setLimiter(Map<K, QuotaLimiter> limiters, final K key, 118 final Quotas quotas) { 119 if (limiters == null) { 120 limiters = new HashMap<>(); 121 } 122 123 QuotaLimiter limiter = 124 quotas.hasThrottle() ? QuotaLimiterFactory.fromThrottle(quotas.getThrottle()) : null; 125 if (limiter != null && !limiter.isBypass()) { 126 limiters.put(key, limiter); 127 } else { 128 limiters.remove(key); 129 } 130 return limiters; 131 } 132 133 /** 134 * Perform an update of the quota state based on the other quota state object. (This operation is 135 * executed by the QuotaCache) 136 */ 137 @Override 138 public synchronized void update(final QuotaState other) { 139 super.update(other); 140 141 if (other instanceof UserQuotaState) { 142 UserQuotaState uOther = (UserQuotaState) other; 143 tableLimiters = updateLimiters(tableLimiters, uOther.tableLimiters); 144 namespaceLimiters = updateLimiters(namespaceLimiters, uOther.namespaceLimiters); 145 bypassGlobals = uOther.bypassGlobals; 146 } else { 147 tableLimiters = null; 148 namespaceLimiters = null; 149 bypassGlobals = false; 150 } 151 } 152 153 private static <K> Map<K, QuotaLimiter> updateLimiters(final Map<K, QuotaLimiter> map, 154 final Map<K, QuotaLimiter> otherMap) { 155 if (map == null) { 156 return otherMap; 157 } 158 159 if (otherMap != null) { 160 // To Remove 161 Set<K> toRemove = new HashSet<>(map.keySet()); 162 toRemove.removeAll(otherMap.keySet()); 163 map.keySet().removeAll(toRemove); 164 165 // To Update/Add 166 for (final Map.Entry<K, QuotaLimiter> entry : otherMap.entrySet()) { 167 QuotaLimiter limiter = map.get(entry.getKey()); 168 if (limiter == null) { 169 limiter = entry.getValue(); 170 } else { 171 limiter = QuotaLimiterFactory.update(limiter, entry.getValue()); 172 } 173 map.put(entry.getKey(), limiter); 174 } 175 return map; 176 } 177 return null; 178 } 179 180 /** 181 * Return the limiter for the specified table associated with this quota. If the table does not 182 * have its own quota limiter the global one will be returned. In case there is no quota limiter 183 * associated with this object a noop limiter will be returned. 184 * @return the quota limiter for the specified table 185 */ 186 public synchronized QuotaLimiter getTableLimiter(final TableName table) { 187 if (tableLimiters != null) { 188 QuotaLimiter limiter = tableLimiters.get(table); 189 if (limiter != null) return limiter; 190 } 191 if (namespaceLimiters != null) { 192 QuotaLimiter limiter = namespaceLimiters.get(table.getNamespaceAsString()); 193 if (limiter != null) return limiter; 194 } 195 return getGlobalLimiter(); 196 } 197}