org.hillview.RedoLog.java Source code

Java tutorial

Introduction

Here is the source code for org.hillview.RedoLog.java

Source

/*
 * Copyright (c) 2018 VMware Inc. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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 org.hillview;

import com.google.gson.JsonElement;
import com.google.gson.JsonStreamParser;
import org.hillview.dataset.api.IJson;
import org.hillview.utils.HillviewLogger;

import javax.annotation.Nullable;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * The Redo log stores information about how RpcTarget objects have been created.
 * The log can be stored to persistent storage.
 * On start-up the log is read from persistent storage allowing sessions
 * to persist across system restarts.
 */
class RedoLog {
    private static final String defaultStorageFile = "hillview.redo";

    /**
     * For each object id the computation that has produced it.
     */
    private final HashMap<RpcTarget.Id, HillviewComputation> generator = new HashMap<RpcTarget.Id, HillviewComputation>();
    /**
     * Map object id to object.
     */
    private final HashMap<RpcTarget.Id, RpcTarget> objects;
    /**
     * File storing this redo log.  If null there is no persistent storage.
     */
    @Nullable
    private final String backupFile;

    RedoLog() {
        this.objects = new HashMap<RpcTarget.Id, RpcTarget>();
        this.backupFile = defaultStorageFile;
        this.reload();
    }

    synchronized public void addObject(RpcTarget target) {
        if (this.objects.containsKey(target.getId()))
            throw new RuntimeException("Object with id " + target.getId() + " already in map");
        this.generator.put(target.getId(), target.computation);
        this.persistInLog(target);
        HillviewLogger.instance.info("Inserting targetId", "{0}", target.toString());
        this.objects.put(target.getId(), target);
    }

    private static class DestAndRequest implements IJson {
        final String resultId;
        final RpcRequest request;

        DestAndRequest(String resultId, RpcRequest request) {
            this.request = request;
            this.resultId = resultId;
        }
    }

    private synchronized void addGenerated(String id, RpcRequest request) {
        HillviewLogger.instance.info("Installing object lineage", "{0} from {1}", id, request);
        RpcTarget.Id tid = new RpcTarget.Id(id);
        this.generator.put(tid, new HillviewComputation(tid, request));
    }

    private void reload() {
        if (this.backupFile == null)
            return;
        HillviewLogger.instance.info("Replaying redo log");
        try (FileReader fr = new FileReader(this.backupFile)) {
            JsonStreamParser parser = new JsonStreamParser(fr);
            while (parser.hasNext()) {
                JsonElement elem = parser.next();
                DestAndRequest dar = IJson.gsonInstance.fromJson(elem, DestAndRequest.class);
                this.addGenerated(dar.resultId, dar.request);
            }
        } catch (IOException ex) {
            HillviewLogger.instance.error("Cannot read hillview redo log", ex);
        }
    }

    private void persistInLog(RpcTarget target) {
        // Yes, we write the data and close the file immediately.
        if (this.backupFile == null)
            return;
        if (target.computation == null)
            return;
        DestAndRequest dar = new DestAndRequest(target.getId().toString(), target.computation.request);
        try (FileWriter f = new FileWriter(this.backupFile, true); PrintWriter p = new PrintWriter(f)) {
            p.println(dar.toJson());
        } catch (IOException ex) {
            HillviewLogger.instance.error("Cannot write to redo log", ex);
        }
    }

    synchronized @Nullable RpcTarget getObject(RpcTarget.Id id) {
        HillviewLogger.instance.info("Getting object", "{0}", id);
        return this.objects.get(id);
    }

    synchronized void deleteObject(RpcTarget.Id id) {
        if (id.isInitial()) {
            HillviewLogger.instance.error("Cannot delete object 0");
            return;
        }
        if (!this.objects.containsKey(id))
            throw new RuntimeException("Object with id " + id + " does not exist");
        this.objects.remove(id);
    }

    /**
     * Removes all RemoteObjects from the cache, except the specified (initial) object.
     * @return  The number of objects removed.
     */
    public int removeAllObjects(RpcTarget.Id except) {
        List<RpcTarget.Id> toDelete = new ArrayList<RpcTarget.Id>();
        for (RpcTarget.Id k : this.objects.keySet()) {
            if (!k.equals(except))
                toDelete.add(k);
        }

        for (RpcTarget.Id k : toDelete)
            this.deleteObject(k);
        return toDelete.size();
    }

    @Nullable
    public HillviewComputation getComputation(RpcTarget.Id id) {
        return this.generator.get(id);
    }
}