001/* 002 * Copyright (c) 2023. Baidu, Inc. All Rights Reserved. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * Unless required by applicable law or agreed to in writing, 009 * software distributed under the License is distributed on an "AS IS" BASIS, 010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 011 * See the License for the specific language governing permissions and limitations under the License. 012 */ 013 014package com.baidu.bifromq.plugin.settingprovider; 015 016import com.github.benmanes.caffeine.cache.Cache; 017import com.github.benmanes.caffeine.cache.Caffeine; 018import com.github.benmanes.caffeine.cache.Scheduler; 019import java.time.Duration; 020import java.util.function.Predicate; 021import lombok.extern.slf4j.Slf4j; 022 023@Slf4j 024public enum Setting { 025 DebugModeEnabled(Boolean.class, val -> true, false), 026 BoostModeEnabled(Boolean.class, val -> true, false), 027 MaxTopicLevelLength(Integer.class, val -> (int) val > 0, 40), 028 MaxTopicLevels(Integer.class, val -> (int) val > 0, 16), 029 MaxTopicLength(Integer.class, val -> (int) val > 0 && (int) val < 65536, 255), 030 MaxSharedGroupMembers(Integer.class, val -> (int) val > 0, 200), 031 MaxTopicFiltersPerInbox(Integer.class, val -> (int) val > 0, 100), 032 MsgPubPerSec(Integer.class, val -> (int) val >= 0 && (int) val <= 1000, 200), 033 InBoundBandWidth(Long.class, val -> (long) val >= 0, 512 * 1024L), 034 OutBoundBandWidth(Long.class, val -> (long) val >= 0, 512 * 1024L), 035 ForceTransient(Boolean.class, val -> true, false), 036 ByPassPermCheckError(Boolean.class, val -> true, true), 037 MaxUserPayloadBytes(Integer.class, val -> (int) val > 0 && (int) val <= 1024 * 1024, 256 * 1024), 038 MaxTopicFiltersPerSub(Integer.class, val -> (int) val > 0 && (int) val <= 100, 10), 039 OfflineExpireTimeSeconds(Long.class, val -> (long) val > 0, 24 * 60 * 60L), 040 OfflineQueueSize(Integer.class, val -> (int) val > 0 && (int) val <= 100000, 1000), 041 OfflineOverflowDropOldest(Boolean.class, val -> true, false), 042 RetainedTopicLimit(Integer.class, val -> (int) val >= 0, 10), 043 RetainMessageMatchLimit(Integer.class, val -> (int) val >= 0, 10), 044 RetainEnabled(Boolean.class, val -> true, true), 045 DistReservedUnitInterval(Long.class, val -> (long) val > 0 && (long) val <= 0xFFFFFFFFL, 0xFFFFFFFFL), 046 DistLimitUnitInterval(Long.class, val -> (long) val >= 0 && (long) val <= 0xFFFFFFFFL, 0L); 047 048 public final Class<?> valueType; 049 final Predicate<Object> validator; 050 final Object initial; 051 final Cache<String, Object> currentVals; 052 053 Setting(Class<?> valueType, Predicate<Object> validator, Object initial) { 054 this.valueType = valueType; 055 this.validator = validator; 056 initial = resolve(initial); 057 assert isValid(initial); 058 this.initial = initial; 059 currentVals = Caffeine.newBuilder() 060 .expireAfterWrite(Duration.ofSeconds(5)) 061 .scheduler(Scheduler.systemScheduler()) 062 .build(); 063 } 064 065 /** 066 * The current effective setting's value for given tenant 067 * 068 * @param tenantId the id of the calling tenant 069 * @return The effective value of the setting for the client 070 */ 071 public <R> R current(String tenantId) { 072 return (R) currentVals.asMap().getOrDefault(tenantId, initial); 073 } 074 075 /** 076 * Validate if provided value is a valid for the setting 077 * 078 * @param val the setting value to be verified 079 * @return true if the value is valid 080 */ 081 public <R> boolean isValid(R val) { 082 if (!valueType.isInstance(val)) { 083 return false; 084 } 085 return this.validator.test(val); 086 } 087 088 // intentionally package level access 089 void current(String tenantId, Object newVal) { 090 assert isValid(newVal); 091 if (!newVal.equals(initial)) { 092 // only cache changed value 093 currentVals.put(tenantId, newVal); 094 } else { 095 // revert to initial 096 currentVals.invalidate(tenantId); 097 } 098 } 099 100 Object resolve(Object initial) { 101 String override = System.getProperty(name()); 102 if (override != null) { 103 try { 104 if (valueType == Integer.class) { 105 return Integer.parseInt(override); 106 } 107 if (valueType == Long.class) { 108 return Long.parseLong(override); 109 } 110 if (valueType == Boolean.class) { 111 return Boolean.parseBoolean(override); 112 } 113 } catch (Throwable e) { 114 log.error("Unable to parse setting value from system property: setting={}, value={}", 115 name(), override); 116 return initial; 117 } 118 } 119 return initial; 120 } 121}