001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to you under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.hadoop.hbase.quotas; 018 019import java.util.Collections; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.Objects; 023 024import org.apache.hadoop.hbase.TableName; 025import org.apache.yetus.audience.InterfaceAudience; 026import org.apache.yetus.audience.InterfaceStability; 027import org.apache.hadoop.hbase.regionserver.Region; 028import org.apache.hadoop.hbase.regionserver.RegionServerServices; 029 030/** 031 * A class to ease dealing with tables that have and do not have violation policies 032 * being enforced. This class is immutable, expect for {@code locallyCachedPolicies}. 033 * 034 * The {@code locallyCachedPolicies} are mutable given the current {@code activePolicies} 035 * and {@code snapshots}. It is expected that when a new instance of this class is 036 * instantiated, we also want to invalidate those previously cached policies (as they 037 * may now be invalidate if we received new quota usage information). 038 */ 039@InterfaceAudience.Private 040@InterfaceStability.Evolving 041public class ActivePolicyEnforcement { 042 private final Map<TableName,SpaceViolationPolicyEnforcement> activePolicies; 043 private final Map<TableName,SpaceQuotaSnapshot> snapshots; 044 private final RegionServerServices rss; 045 private final SpaceViolationPolicyEnforcementFactory factory; 046 private final Map<TableName,SpaceViolationPolicyEnforcement> locallyCachedPolicies; 047 048 public ActivePolicyEnforcement(Map<TableName,SpaceViolationPolicyEnforcement> activePolicies, 049 Map<TableName,SpaceQuotaSnapshot> snapshots, RegionServerServices rss) { 050 this(activePolicies, snapshots, rss, SpaceViolationPolicyEnforcementFactory.getInstance()); 051 } 052 053 public ActivePolicyEnforcement(Map<TableName,SpaceViolationPolicyEnforcement> activePolicies, 054 Map<TableName,SpaceQuotaSnapshot> snapshots, RegionServerServices rss, 055 SpaceViolationPolicyEnforcementFactory factory) { 056 this.activePolicies = activePolicies; 057 this.snapshots = snapshots; 058 this.rss = rss; 059 this.factory = factory; 060 // Mutable! 061 this.locallyCachedPolicies = new HashMap<>(); 062 } 063 064 /** 065 * Returns the proper {@link SpaceViolationPolicyEnforcement} implementation for the given table. 066 * If the given table does not have a violation policy enforced, a "no-op" policy will 067 * be returned which always allows an action. 068 * 069 * @see #getPolicyEnforcement(TableName) 070 */ 071 public SpaceViolationPolicyEnforcement getPolicyEnforcement(Region r) { 072 return getPolicyEnforcement(Objects.requireNonNull(r).getTableDescriptor().getTableName()); 073 } 074 075 /** 076 * Returns the proper {@link SpaceViolationPolicyEnforcement} implementation for the given table. 077 * If the given table does not have a violation policy enforced, a "no-op" policy will 078 * be returned which always allows an action. 079 * 080 * @param tableName The table to fetch the policy for. 081 * @return A non-null {@link SpaceViolationPolicyEnforcement} instance. 082 */ 083 public SpaceViolationPolicyEnforcement getPolicyEnforcement(TableName tableName) { 084 SpaceViolationPolicyEnforcement policy = activePolicies.get(Objects.requireNonNull(tableName)); 085 if (policy == null) { 086 synchronized (locallyCachedPolicies) { 087 // When we don't have an policy enforcement for the table, there could be one of two cases: 088 // 1) The table has no quota defined 089 // 2) The table is not in violation of its quota 090 // In both of these cases, we want to make sure that access remains fast and we minimize 091 // object creation. We can accomplish this by locally caching policies instead of creating 092 // a new instance of the policy each time. 093 policy = locallyCachedPolicies.get(tableName); 094 // We have already created/cached the enforcement, use it again. `activePolicies` and 095 // `snapshots` are immutable, thus this policy is valid for the lifetime of `this`. 096 if (policy != null) { 097 return policy; 098 } 099 // Create a PolicyEnforcement for this table and snapshot. The snapshot may be null 100 // which is OK. 101 policy = factory.createWithoutViolation(rss, tableName, snapshots.get(tableName)); 102 // Cache the policy we created 103 locallyCachedPolicies.put(tableName, policy); 104 } 105 } 106 return policy; 107 } 108 109 /** 110 * Returns an unmodifiable version of the active {@link SpaceViolationPolicyEnforcement}s. 111 */ 112 public Map<TableName,SpaceViolationPolicyEnforcement> getPolicies() { 113 return Collections.unmodifiableMap(activePolicies); 114 } 115 116 /** 117 * Returns an unmodifiable version of the policy enforcements that were cached because they are 118 * not in violation of their quota. 119 */ 120 Map<TableName,SpaceViolationPolicyEnforcement> getLocallyCachedPolicies() { 121 return Collections.unmodifiableMap(locallyCachedPolicies); 122 } 123 124 @Override 125 public String toString() { 126 return getClass().getSimpleName() + ": " + activePolicies; 127 } 128}