All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.weibo.rill.flow.service.storage.RuntimeStorage Maven / Gradle / Ivy

/*
 *  Copyright 2021-2023 Weibo, Inc.
 *
 *    Licensed 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 com.weibo.rill.flow.service.storage;

import com.google.common.collect.Maps;
import com.weibo.rill.flow.interfaces.model.task.TaskInfo;
import com.weibo.rill.flow.olympicene.core.constant.ReservedConstant;
import com.weibo.rill.flow.olympicene.core.helper.DAGWalkHelper;
import com.weibo.rill.flow.olympicene.core.model.dag.DAG;
import com.weibo.rill.flow.olympicene.core.model.dag.DAGInfo;
import com.weibo.rill.flow.olympicene.core.runtime.DAGContextStorage;
import com.weibo.rill.flow.olympicene.core.runtime.DAGInfoStorage;
import com.weibo.rill.flow.olympicene.core.switcher.SwitcherManager;
import com.weibo.rill.flow.olympicene.storage.constant.StorageErrorCode;
import com.weibo.rill.flow.olympicene.storage.exception.StorageException;
import com.weibo.rill.flow.olympicene.storage.redis.api.RedisClient;
import com.weibo.rill.flow.olympicene.storage.save.impl.DAGInfoDeserializeService;
import com.weibo.rill.flow.olympicene.storage.save.impl.DAGRedisStorage;
import com.weibo.rill.flow.service.dconfs.BizDConfs;
import com.weibo.rill.flow.service.storage.dao.ContextRedisDAO;
import com.weibo.rill.flow.service.storage.dao.DAGInfoRedisDAO;
import com.weibo.rill.flow.service.util.ExecutionIdUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;


@Slf4j
public class RuntimeStorage implements DAGInfoStorage, DAGContextStorage {
    private final DAGRedisStorage runtimeRedisStorage;
    private final RuntimeSwapStorage runtimeSwapStorage;
    private final BizDConfs bizDConfs;

    public RuntimeStorage(RedisClient redisClient, Map clientIdToRedisClient, BizDConfs bizDConfs,
                          DAGInfoDeserializeService dagInfoDeserializeService, SwitcherManager switcherManagerImpl) {
        this.bizDConfs = bizDConfs;

        DAGInfoRedisDAO dagInfoRedisDAO = new DAGInfoRedisDAO(redisClient, bizDConfs, dagInfoDeserializeService);
        ContextRedisDAO contextRedisDAO = new ContextRedisDAO(redisClient, bizDConfs, switcherManagerImpl);
        this.runtimeRedisStorage = new DAGRedisStorage(dagInfoRedisDAO, contextRedisDAO);

        this.runtimeSwapStorage = new RuntimeSwapStorage(redisClient, clientIdToRedisClient, bizDConfs);
    }

    @Override
    public void saveDAGInfo(String executionId, DAGInfo dagInfo) {
        if (dagInfo == null) {
            return;
        }

        Runnable redisOperation = () -> runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
        Runnable swapOperation = () -> runtimeSwapStorage.saveDAGInfo(executionId, dagInfo);
        setAction(executionId, redisOperation, swapOperation);
    }

    @Override
    public void saveTaskInfos(String executionId, Set taskInfos) {
        if (CollectionUtils.isEmpty(taskInfos)) {
            log.info("saveTaskInfos taskInfos empty, executionId:{}, taskInfos:{}", executionId, taskInfos);
            return;
        }

        Runnable redisOperation = () -> runtimeRedisStorage.saveTaskInfos(executionId, taskInfos);
        Runnable swapOperation = () -> runtimeSwapStorage.saveTaskInfos(executionId, taskInfos);
        setAction(executionId, redisOperation, swapOperation);
    }

    @Override
    public DAGInfo getDAGInfo(String executionId) {
        Supplier redisOperation = () -> runtimeRedisStorage.getDAGInfo(executionId);
        Function isValueAcquired = Objects::nonNull;
        Supplier swapOperation = () -> {
            DAGInfo dagInfo = runtimeSwapStorage.getDAGInfo(executionId);
            if (dagInfo != null) {
                runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
            }
            return dagInfo;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public DAGInfo getBasicDAGInfo(String executionId) {
        Supplier redisOperation = () -> runtimeRedisStorage.getBasicDAGInfo(executionId);
        Function isValueAcquired = Objects::nonNull;
        Supplier swapOperation = () -> {
            DAGInfo dagInfo = runtimeSwapStorage.getDAGInfo(executionId);
            if (dagInfo != null) {
                runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
                Optional.ofNullable(dagInfo.getTasks()).map(Map::values)
                        .ifPresent(taskInfos -> taskInfos.forEach(taskInfo -> taskInfo.setChildren(new LinkedHashMap<>())));
            }
            return dagInfo;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public TaskInfo getBasicTaskInfo(String executionId, String taskName) {
        Supplier redisOperation = () -> runtimeRedisStorage.getBasicTaskInfo(executionId, taskName);
        Function isValueAcquired = Objects::nonNull;
        Supplier swapOperation = () -> {
            TaskInfo taskInfo = null;
            DAGInfo dagInfo = runtimeSwapStorage.getDAGInfo(executionId);
            if (dagInfo != null) {
                runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
                taskInfo = DAGWalkHelper.getInstance().getTaskInfoByName(dagInfo, taskName);
                taskInfo.setChildren(new LinkedHashMap<>());
            }
            return taskInfo;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public TaskInfo getTaskInfo(String executionId, String taskName) {
        Supplier redisOperation = () -> runtimeRedisStorage.getTaskInfo(executionId, taskName);
        Function isValueAcquired = Objects::nonNull;
        Supplier swapOperation = () -> {
            TaskInfo taskInfo = null;
            DAGInfo dagInfo = runtimeSwapStorage.getDAGInfo(executionId);
            if (dagInfo != null) {
                runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
                taskInfo = DAGWalkHelper.getInstance().getTaskInfoByName(dagInfo, taskName);
            }
            return taskInfo;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public TaskInfo getParentTaskInfoWithSibling(String executionId, String taskName) {
        Supplier redisOperation = () -> runtimeRedisStorage.getParentTaskInfoWithSibling(executionId, taskName);
        Function isValueAcquired = Objects::nonNull;
        Supplier swapOperation = () -> {
            TaskInfo taskInfo = null;
            DAGInfo dagInfo = runtimeSwapStorage.getDAGInfo(executionId);
            if (dagInfo != null) {
                runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
                TaskInfo currentTaskInfo = DAGWalkHelper.getInstance().getTaskInfoByName(dagInfo, taskName);
                TaskInfo parentTaskInfo = currentTaskInfo.getParent();
                Map sibling = parentTaskInfo.getChildren().entrySet().stream()
                        .filter(entry -> entry.getValue().getRouteName().equals(currentTaskInfo.getRouteName()))
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
                parentTaskInfo.setChildren(sibling);
                taskInfo = parentTaskInfo;
            }
            return taskInfo;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public void clearDAGInfo(String executionId) {
        Runnable redisOperation = () -> runtimeRedisStorage.clearDAGInfo(executionId);
        Runnable swapOperation = () -> runtimeSwapStorage.clearDAGInfo(executionId);
        setAction(executionId, redisOperation, swapOperation);
    }

    @Override
    public void clearDAGInfo(String executionId, int expireTimeInSecond) {
        Runnable redisOperation = () -> runtimeRedisStorage.clearDAGInfo(executionId, expireTimeInSecond);
        Runnable swapOperation = () -> runtimeSwapStorage.clearDAGInfo(executionId);
        setAction(executionId, redisOperation, swapOperation);
    }

    @Override
    public DAG getDAGDescriptor(String executionId) {
        Supplier redisOperation = () -> runtimeRedisStorage.getDAGDescriptor(executionId);
        Function isValueAcquired = Objects::nonNull;
        Supplier swapOperation = () -> {
            DAG dag = null;
            DAGInfo dagInfo = runtimeSwapStorage.getDAGInfo(executionId);
            if (dagInfo != null) {
                runtimeRedisStorage.saveDAGInfo(executionId, dagInfo);
                dag = dagInfo.getDag();
            }
            return dag;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public void updateDAGDescriptor(String executionId, DAG dag) {
        if (dag == null) {
            return;
        }

        Runnable redisOperation = () -> runtimeRedisStorage.updateDAGDescriptor(executionId, dag);
        Runnable swapOperation = () -> runtimeSwapStorage.updateDAGDescriptor(executionId, dag);
        setAction(executionId, redisOperation, swapOperation);
    }

    @Override
    public void updateContext(String executionId, Map context) {
        if (MapUtils.isEmpty(context)) {
            return;
        }

        Runnable redisOperation = () -> runtimeRedisStorage.updateContext(executionId, context);
        Runnable swapOperation = () -> runtimeSwapStorage.updateContext(executionId, context);
        setAction(executionId, redisOperation, swapOperation);
    }

    @Override
    public Map getContext(String executionId) {
        Supplier> redisOperation = () -> runtimeRedisStorage.getContext(executionId);
        Function, Boolean> isValueAcquired = MapUtils::isNotEmpty;
        Supplier> swapOperation = () -> {
            Map ret = Maps.newHashMap();
            Map totalContext = runtimeSwapStorage.getTotalContext(executionId);
            if (MapUtils.isNotEmpty(totalContext)) {
                runtimeRedisStorage.updateContext(executionId, totalContext);
                ret = totalContext.entrySet().stream()
                        .filter(entry -> !entry.getKey().startsWith(ReservedConstant.SUB_CONTEXT_PREFIX))
                        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
            }
            return ret;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public Map getContext(String executionId, Collection fields) {
        if (CollectionUtils.isEmpty(fields)) {
            return Maps.newHashMap();
        }

        Supplier> redisOperation = () -> runtimeRedisStorage.getContext(executionId, fields);
        Function, Boolean> isValueAcquired = MapUtils::isNotEmpty;
        Supplier> swapOperation = () -> {
            Map ret = Maps.newHashMap();
            Map totalContext = runtimeSwapStorage.getTotalContext(executionId);
            if (MapUtils.isNotEmpty(totalContext)) {
                runtimeRedisStorage.updateContext(executionId, totalContext);
                fields.forEach(field -> Optional.ofNullable(totalContext.get(field))
                        .ifPresent(value -> ret.put(field, value)));
            }
            return ret;
        };
        return getAction(executionId, redisOperation, isValueAcquired, swapOperation);
    }

    @Override
    public void clearContext(String executionId) {
        Runnable redisOperation = () -> runtimeRedisStorage.clearContext(executionId);
        Runnable swapOperation = () -> runtimeSwapStorage.clearContext(executionId);
        setAction(executionId, redisOperation, swapOperation);
    }

    private  T getAction(String executionId, Supplier redisOperation, Function isValueAcquired, Supplier swapOperation) {
        if (!swapExist(executionId)) {
            return redisOperation.get();
        }

        T redisValue;
        try {
            redisValue = redisOperation.get();
            if (isValueAcquired.apply(redisValue)) {
                return redisValue;
            }
        } catch (Exception e) {
            if (e instanceof StorageException &&
                    ((StorageException) e).getErrorCode() == StorageErrorCode.DAG_LENGTH_LIMITATION.getCode()) {
                throw e;
            }

            T swapValue = getSwapValue(executionId, swapOperation);
            if (isValueAcquired.apply(swapValue)) {
                return swapValue;
            }
            throw e;
        }

        T swapValue = getSwapValue(executionId, swapOperation);
        return isValueAcquired.apply(swapValue) ? swapValue : redisValue;
    }

    private  T getSwapValue(String executionId, Supplier swapOperation) {
        try {
            log.info("get value from swap storage executionId:{}", executionId);
            return swapOperation.get();
        } catch (Exception e) {
            log.warn("getSwapValue fails, executionId:{}", executionId, e);
            return null;
        }
    }

    private void setAction(String executionId, Runnable redisOperation, Runnable swapOperation) {
        redisOperation.run();

        if (swapExist(executionId)) {
            try {
                swapOperation.run();
            } catch (Exception e) {
                log.warn("setAction fails, executionId:{}", executionId, e);
            }
        }
    }

    private boolean swapExist(String executionId) {
        try {
            String businessId = ExecutionIdUtil.getBusinessId(executionId);
            return bizDConfs.getSwapBusinessIdToClientId().containsKey(businessId);
        } catch (Exception e) {
            log.warn("swapExist fails, executionId:{}, errorMsg:{}", executionId, e.getMessage());
            return false;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy