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.Collections; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.Objects; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.regionserver.Region; 026import org.apache.hadoop.hbase.regionserver.RegionServerServices; 027import org.apache.yetus.audience.InterfaceAudience; 028import org.apache.yetus.audience.InterfaceStability; 029 030/** 031 * A class to ease dealing with tables that have and do not have violation policies being enforced. 032 * This class is immutable, expect for {@code locallyCachedPolicies}. The 033 * {@code locallyCachedPolicies} are mutable given the current {@code activePolicies} and 034 * {@code snapshots}. It is expected that when a new instance of this class is instantiated, we also 035 * want to invalidate those previously cached policies (as they may now be invalidate if we received 036 * new quota usage information). 037 */ 038@InterfaceAudience.Private 039@InterfaceStability.Evolving 040public class ActivePolicyEnforcement { 041 private final Map<TableName, SpaceViolationPolicyEnforcement> activePolicies; 042 private final Map<TableName, SpaceQuotaSnapshot> snapshots; 043 private final RegionServerServices rss; 044 private final SpaceViolationPolicyEnforcementFactory factory; 045 private final Map<TableName, SpaceViolationPolicyEnforcement> locallyCachedPolicies; 046 047 public ActivePolicyEnforcement(Map<TableName, SpaceViolationPolicyEnforcement> activePolicies, 048 Map<TableName, SpaceQuotaSnapshot> snapshots, RegionServerServices rss) { 049 this(activePolicies, snapshots, rss, SpaceViolationPolicyEnforcementFactory.getInstance()); 050 } 051 052 public ActivePolicyEnforcement(Map<TableName, SpaceViolationPolicyEnforcement> activePolicies, 053 Map<TableName, SpaceQuotaSnapshot> snapshots, RegionServerServices rss, 054 SpaceViolationPolicyEnforcementFactory factory) { 055 this.activePolicies = activePolicies; 056 this.snapshots = snapshots; 057 this.rss = rss; 058 this.factory = factory; 059 // Mutable! 060 this.locallyCachedPolicies = new HashMap<>(); 061 } 062 063 /** 064 * Returns the proper {@link SpaceViolationPolicyEnforcement} implementation for the given table. 065 * If the given table does not have a violation policy enforced, a "no-op" policy will be returned 066 * which always allows an action. 067 * @see #getPolicyEnforcement(TableName) 068 */ 069 public SpaceViolationPolicyEnforcement getPolicyEnforcement(Region r) { 070 return getPolicyEnforcement(Objects.requireNonNull(r).getTableDescriptor().getTableName()); 071 } 072 073 /** 074 * Returns the proper {@link SpaceViolationPolicyEnforcement} implementation for the given table. 075 * If the given table does not have a violation policy enforced, a "no-op" policy will be returned 076 * which always allows an action. 077 * @param tableName The table to fetch the policy for. 078 * @return A non-null {@link SpaceViolationPolicyEnforcement} instance. 079 */ 080 public SpaceViolationPolicyEnforcement getPolicyEnforcement(TableName tableName) { 081 SpaceViolationPolicyEnforcement policy = activePolicies.get(Objects.requireNonNull(tableName)); 082 if (policy == null) { 083 synchronized (locallyCachedPolicies) { 084 // When we don't have an policy enforcement for the table, there could be one of two cases: 085 // 1) The table has no quota defined 086 // 2) The table is not in violation of its quota 087 // In both of these cases, we want to make sure that access remains fast and we minimize 088 // object creation. We can accomplish this by locally caching policies instead of creating 089 // a new instance of the policy each time. 090 policy = locallyCachedPolicies.get(tableName); 091 // We have already created/cached the enforcement, use it again. `activePolicies` and 092 // `snapshots` are immutable, thus this policy is valid for the lifetime of `this`. 093 if (policy != null) { 094 return policy; 095 } 096 // Create a PolicyEnforcement for this table and snapshot. The snapshot may be null 097 // which is OK. 098 policy = factory.createWithoutViolation(rss, tableName, snapshots.get(tableName)); 099 // Cache the policy we created 100 locallyCachedPolicies.put(tableName, policy); 101 } 102 } 103 return policy; 104 } 105 106 /** 107 * Returns an unmodifiable version of the active {@link SpaceViolationPolicyEnforcement}s. 108 */ 109 public Map<TableName, SpaceViolationPolicyEnforcement> getPolicies() { 110 return Collections.unmodifiableMap(activePolicies); 111 } 112 113 /** 114 * Returns an unmodifiable version of the policy enforcements that were cached because they are 115 * not in violation of their quota. 116 */ 117 Map<TableName, SpaceViolationPolicyEnforcement> getLocallyCachedPolicies() { 118 return Collections.unmodifiableMap(locallyCachedPolicies); 119 } 120 121 @Override 122 public String toString() { 123 return getClass().getSimpleName() + ": " + activePolicies; 124 } 125}