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

step.artefacts.handlers.functions.FunctionGroupSession Maven / Gradle / Ivy

/*
 * Copyright (C) 2024, exense GmbH
 *
 * This file is part of Step
 *
 * Step is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Step is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Step.  If not, see .
 */

package step.artefacts.handlers.functions;

import step.common.managedoperations.OperationManager;
import step.functions.execution.FunctionExecutionService;
import step.functions.execution.FunctionExecutionServiceException;
import step.grid.TokenPretender;
import step.grid.TokenWrapper;
import step.grid.TokenWrapperOwner;
import step.grid.tokenpool.Identity;
import step.grid.tokenpool.Interest;
import step.grid.tokenpool.SimpleAffinityEvaluator;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * This class implements the underlying logic of Session artefacts
 * It caches reserved tokens and reuse them for subsequent executions whenever it is possible
 */
public class FunctionGroupSession implements AutoCloseable {

    private final long ownerThreadId;
    private final FunctionExecutionService functionExecutionService;
    private final SimpleAffinityEvaluator affinityEvaluator = new SimpleAffinityEvaluator<>();

    private final List tokens = new ArrayList<>();
    private TokenWrapper localToken;

    public FunctionGroupSession(FunctionExecutionService functionExecutionService) {
        this.functionExecutionService = functionExecutionService;
        this.ownerThreadId = Thread.currentThread().getId();
    }

    public TokenWrapper getLocalToken() {
        if (localToken == null) {
            localToken = functionExecutionService.getLocalTokenHandle();
        }
        return localToken;
    }

    public synchronized TokenWrapper getRemoteToken(Map tokenSelectionCriteria, TokenWrapperOwner tokenWrapperOwner) throws FunctionExecutionServiceException {
        return getRemoteToken(Map.of(), tokenSelectionCriteria, tokenWrapperOwner, true);
    }

    public synchronized TokenWrapper getRemoteToken(Map ownAttributes, Map tokenSelectionCriteria, TokenWrapperOwner tokenWrapperOwner, boolean createRemoteSession) throws FunctionExecutionServiceException {
        if (!isCurrentThreadOwner()) {
            throw new RuntimeException("Tokens from this sesssion are already reserved by another thread. This usually means that you're spawning threads from wihtin a session control without creating new sessions for the new threads.");
        }

        // Find a token matching the selection criteria in the context
        TokenWrapper matchingToken = tokens.stream().filter(t ->
                affinityEvaluator.getAffinityScore(new TokenPretender(Map.of(), tokenSelectionCriteria), new TokenPretender(t.getAttributes(), Map.of())) > 0).findFirst().orElse(null);

        TokenWrapper token;
        if (matchingToken != null) {
            // Token already present in context => reusing it
            token = matchingToken;
        } else {
            // No token matching the selection criteria => select a new token and add it to the function group context
            token = selectToken(ownAttributes, tokenSelectionCriteria, tokenWrapperOwner, createRemoteSession);
            tokens.add(token);
        }
        return token;
    }

    private boolean isCurrentThreadOwner() {
        return Thread.currentThread().getId() == ownerThreadId;
    }

    private TokenWrapper selectToken(Map ownAttributes, Map selectionCriteria, TokenWrapperOwner tokenWrapperOwner, boolean createRemoteSession) throws FunctionExecutionServiceException {

        TokenWrapper token;
        OperationManager.getInstance().enter("Token selection", selectionCriteria);
        try {
            token = functionExecutionService.getTokenHandle(ownAttributes, selectionCriteria, createRemoteSession, tokenWrapperOwner);
        } finally {
            OperationManager.getInstance().exit();
        }
        return token;
    }

    @Override
    public void close() throws Exception {
        releaseTokens(true);
    }

    public void releaseTokens(boolean alsoReleaseLocalTokens) throws Exception {
        List releaseExceptions = new ArrayList<>();
        tokens.forEach(t -> {
            try {
                functionExecutionService.returnTokenHandle(t.getID());
            } catch (FunctionExecutionServiceException e) {
                releaseExceptions.add(e);
            }
        });
        tokens.clear();
        if (localToken != null && alsoReleaseLocalTokens) {
            try {
                functionExecutionService.returnTokenHandle(localToken.getID());
            } catch (FunctionExecutionServiceException e) {
                releaseExceptions.add(e);
            } finally {
                localToken = null;
            }
        }

        int exceptionCount = releaseExceptions.size();
        if (exceptionCount > 0) {
            if (exceptionCount == 1) {
                throw releaseExceptions.get(0);
            } else {
                throw new Exception("Multiple errors occurred when releasing agent tokens: " +
                        releaseExceptions.stream().map(Throwable::getMessage).collect(Collectors.joining(", ")));
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy