| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.servicecomb.injection; |
| |
| import java.util.Map; |
| import java.util.concurrent.atomic.AtomicLong; |
| |
| import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; |
| import org.apache.servicecomb.governance.policy.FaultInjectionPolicy; |
| |
| import io.vertx.core.Context; |
| import io.vertx.core.Vertx; |
| |
| /** |
| * Handles the count for all request based key[transport + microservice qualified name]. |
| */ |
| public final class FaultInjectionUtil { |
| |
| private FaultInjectionUtil() { |
| } |
| |
| /** |
| * key is transport+operQualifiedName |
| */ |
| private static final Map<String, AtomicLong> REQUEST_COUNT = new ConcurrentHashMapEx<>(); |
| |
| /** |
| * Returns total requests per provider for operational level. |
| * |
| * @param key |
| * transport+operational name |
| * @return long total requests |
| */ |
| public static AtomicLong getOperMetTotalReq(String key) { |
| return REQUEST_COUNT.computeIfAbsent(key, p -> new AtomicLong(1)); |
| } |
| |
| /** |
| * It will check the delay/abort condition based on request count and percentage |
| * received. |
| * |
| * @param reqCount total request count of the uri |
| * @param percentage the percentage of hitting fault injection |
| * @return true: delay/abort is needed. false: delay/abort is not needed. |
| */ |
| public static boolean isFaultNeedToInject(long reqCount, int percentage) { |
| /* |
| * Example: delay/abort percentage configured is 10% and Get the count(suppose |
| * if it is 10th request) from map and calculate resultNew(10th request) and |
| * requestOld(9th request). Like this for every request it will calculate |
| * current request count and previous count. if both not matched need to add |
| * delay/abort otherwise no need to add. |
| */ |
| |
| // calculate the value with current request count. |
| long resultNew = (reqCount * percentage) / 100; |
| |
| // calculate the value with previous count value. |
| long resultOld = ((reqCount - 1) * percentage) / 100; |
| |
| // if both are not matching then delay/abort should be added. |
| return (resultNew != resultOld); |
| } |
| |
| public static Fault getFault(String key, FaultInjectionPolicy policy) { |
| Fault fault = null; |
| if (FaultInjectionConst.TYPE_DELAY.equals(policy.getType())) { |
| fault = new DelayFault(key, policy); |
| } else if (FaultInjectionConst.TYPE_ABORT.equals(policy.getType())) { |
| fault = new AbortFault(key, policy); |
| } |
| return fault; |
| } |
| |
| public static FaultParam initFaultParam(String key) { |
| AtomicLong reqCount = FaultInjectionUtil.getOperMetTotalReq(key); |
| // increment the request count here after checking the delay/abort condition. |
| long reqCountCurrent = reqCount.getAndIncrement(); |
| |
| FaultParam param = new FaultParam(reqCountCurrent); |
| Context currentContext = Vertx.currentContext(); |
| if (currentContext != null && currentContext.owner() != null && currentContext.isEventLoopContext()) { |
| param.setSleepable( |
| (delay) -> currentContext.owner().setTimer(delay, timeId -> {})); |
| } |
| return param; |
| } |
| |
| } |