org.apache.giraph.master.MasterAggregatorHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.giraph.master.MasterAggregatorHandler.java

Source

/*
 * 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.giraph.master;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.giraph.aggregators.AggregatorWriter;
import org.apache.giraph.bsp.BspService;
import org.apache.giraph.bsp.SuperstepState;
import org.apache.giraph.comm.GlobalCommType;
import org.apache.giraph.comm.MasterClient;
import org.apache.giraph.comm.aggregators.AggregatorUtils;
import org.apache.giraph.conf.ImmutableClassesGiraphConfiguration;
import org.apache.giraph.reducers.ReduceOperation;
import org.apache.giraph.reducers.Reducer;
import org.apache.giraph.utils.WritableUtils;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.Progressable;
import org.apache.log4j.Logger;

import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;

/** Handler for reduce/broadcast on the master */
public class MasterAggregatorHandler implements MasterGlobalCommUsage, Writable {
    /** Class logger */
    private static final Logger LOG = Logger.getLogger(MasterAggregatorHandler.class);

    /** Map of reducers registered for the next worker computation */
    private final Map<String, Reducer<Object, Writable>> reducerMap = Maps.newHashMap();
    /** Map of values to be sent to workers for next computation */
    private final Map<String, Writable> broadcastMap = Maps.newHashMap();
    /** Values reduced from previous computation */
    private final Map<String, Writable> reducedMap = Maps.newHashMap();

    /** Aggregator writer - for writing reduced values */
    private final AggregatorWriter aggregatorWriter;
    /** Progressable used to report progress */
    private final Progressable progressable;

    /** Conf */
    private final ImmutableClassesGiraphConfiguration<?, ?, ?> conf;

    /**
     * Constructor
     *
     * @param conf Configuration
     * @param progressable Progress reporter
     */
    public MasterAggregatorHandler(ImmutableClassesGiraphConfiguration<?, ?, ?> conf, Progressable progressable) {
        this.progressable = progressable;
        this.conf = conf;
        aggregatorWriter = conf.createAggregatorWriter();
    }

    @Override
    public final <S, R extends Writable> void registerReduce(String name, ReduceOperation<S, R> reduceOp) {
        registerReduce(name, reduceOp, reduceOp.createInitialValue());
    }

    @Override
    public <S, R extends Writable> void registerReduce(String name, ReduceOperation<S, R> reduceOp,
            R globalInitialValue) {
        if (reducerMap.containsKey(name)) {
            throw new IllegalArgumentException("Reducer with name " + name + " was already registered, "
                    + " and is " + reducerMap.get(name) + ", and we are trying to " + " register " + reduceOp);
        }
        if (reduceOp == null) {
            throw new IllegalArgumentException("null reducer cannot be registered, with name " + name);
        }
        if (globalInitialValue == null) {
            throw new IllegalArgumentException("global initial value for reducer cannot be null, but is for "
                    + reduceOp + " with naem" + name);
        }

        Reducer<S, R> reducer = new Reducer<>(reduceOp, globalInitialValue);
        reducerMap.put(name, (Reducer<Object, Writable>) reducer);
    }

    @Override
    public <T extends Writable> T getReduced(String name) {
        T value = (T) reducedMap.get(name);
        if (value == null) {
            LOG.warn("getReduced: "
                    + AggregatorUtils.getUnregisteredReducerMessage(name, reducedMap.size() != 0, conf));
        }
        return value;
    }

    @Override
    public void broadcast(String name, Writable object) {
        if (broadcastMap.containsKey(name)) {
            throw new IllegalArgumentException("Value already broadcasted for name " + name);
        }
        if (object == null) {
            throw new IllegalArgumentException("null cannot be broadcasted");
        }

        broadcastMap.put(name, object);
    }

    /** Prepare reduced values for current superstep's master compute */
    public void prepareSuperstep() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("prepareSuperstep: Start preparing reducers");
        }

        Preconditions.checkState(reducedMap.isEmpty(), "reducedMap must be empty before start of the superstep");
        Preconditions.checkState(broadcastMap.isEmpty(),
                "broadcastMap must be empty before start of the superstep");

        for (Entry<String, Reducer<Object, Writable>> entry : reducerMap.entrySet()) {
            Writable value = entry.getValue().getCurrentValue();
            if (value == null) {
                value = entry.getValue().createInitialValue();
            }

            reducedMap.put(entry.getKey(), value);
        }

        reducerMap.clear();

        if (LOG.isDebugEnabled()) {
            LOG.debug("prepareSuperstep: Aggregators prepared");
        }
    }

    /** Finalize aggregators for current superstep */
    public void finishSuperstep() {
        if (LOG.isDebugEnabled()) {
            LOG.debug("finishSuperstep: Start finishing aggregators");
        }

        reducedMap.clear();

        if (LOG.isDebugEnabled()) {
            LOG.debug("finishSuperstep: Aggregators finished");
        }
    }

    /**
     * Send data to workers (through owner workers)
     *
     * @param masterClient IPC client on master
     */
    public void sendDataToOwners(MasterClient masterClient) {
        // send broadcast values and reduceOperations to their owners
        try {
            for (Entry<String, Reducer<Object, Writable>> entry : reducerMap.entrySet()) {
                masterClient.sendToOwner(entry.getKey(), GlobalCommType.REDUCE_OPERATIONS,
                        entry.getValue().getReduceOp());
                progressable.progress();
            }

            for (Entry<String, Writable> entry : broadcastMap.entrySet()) {
                masterClient.sendToOwner(entry.getKey(), GlobalCommType.BROADCAST, entry.getValue());
                progressable.progress();
            }
            masterClient.finishSendingValues();

            broadcastMap.clear();
        } catch (IOException e) {
            throw new IllegalStateException("finishSuperstep: " + "IOException occurred while sending aggregators",
                    e);
        }
    }

    /**
     * Accept reduced values sent by worker. Every value will be sent
     * only once, by its owner.
     * We don't need to count the number of these requests because global
     * superstep barrier will happen after workers ensure all requests of this
     * type have been received and processed by master.
     *
     * @param reducedValuesInput Input in which aggregated values are
     *                              written in the following format:
     *                              numReducers
     *                              name_1  REDUCED_VALUE  value_1
     *                              name_2  REDUCED_VALUE  value_2
     *                              ...
     * @throws IOException
     */
    public void acceptReducedValues(DataInput reducedValuesInput) throws IOException {
        int numReducers = reducedValuesInput.readInt();
        for (int i = 0; i < numReducers; i++) {
            String name = reducedValuesInput.readUTF();
            GlobalCommType type = GlobalCommType.values()[reducedValuesInput.readByte()];
            if (type != GlobalCommType.REDUCED_VALUE) {
                throw new IllegalStateException("SendReducedToMasterRequest received " + type);
            }
            Reducer<Object, Writable> reducer = reducerMap.get(name);
            if (reducer == null) {
                throw new IllegalStateException(
                        "acceptReducedValues: " + "Master received reduced value which isn't registered: " + name);
            }

            Writable valueToReduce = reducer.createInitialValue();
            valueToReduce.readFields(reducedValuesInput);

            if (reducer.getCurrentValue() != null) {
                reducer.reducePartial(valueToReduce);
            } else {
                reducer.setCurrentValue(valueToReduce);
            }
            progressable.progress();
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("acceptReducedValues: Accepted one set with " + numReducers + " aggregated values");
        }
    }

    /**
     * Write aggregators to {@link AggregatorWriter}
     *
     * @param superstep      Superstep which just finished
     * @param superstepState State of the superstep which just finished
     */
    public void writeAggregators(long superstep, SuperstepState superstepState) {
        try {
            aggregatorWriter.writeAggregator(reducedMap.entrySet(),
                    (superstepState == SuperstepState.ALL_SUPERSTEPS_DONE) ? AggregatorWriter.LAST_SUPERSTEP
                            : superstep);
        } catch (IOException e) {
            throw new IllegalStateException("coordinateSuperstep: IOException while " + "writing aggregators data",
                    e);
        }
    }

    /**
     * Initialize {@link AggregatorWriter}
     *
     * @param service BspService
     */
    public void initialize(BspService service) {
        try {
            aggregatorWriter.initialize(service.getContext(), service.getApplicationAttempt());
        } catch (IOException e) {
            throw new IllegalStateException("initialize: " + "Couldn't initialize aggregatorWriter", e);
        }
    }

    /**
     * Close {@link AggregatorWriter}
     *
     * @throws IOException
     */
    public void close() throws IOException {
        aggregatorWriter.close();
    }

    @Override
    public void write(DataOutput out) throws IOException {
        // At the end of superstep, only reduceOpMap can be non-empty
        Preconditions.checkState(reducedMap.isEmpty(), "reducedMap must be empty at the end of the superstep");

        out.writeInt(reducerMap.size());
        for (Entry<String, Reducer<Object, Writable>> entry : reducerMap.entrySet()) {
            out.writeUTF(entry.getKey());
            entry.getValue().write(out);
            progressable.progress();
        }

        out.writeInt(broadcastMap.size());
        for (Entry<String, Writable> entry : broadcastMap.entrySet()) {
            out.writeUTF(entry.getKey());
            WritableUtils.writeWritableObject(entry.getValue(), out);
        }
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        reducedMap.clear();
        broadcastMap.clear();
        reducerMap.clear();

        int numReducers = in.readInt();
        for (int i = 0; i < numReducers; i++) {
            String name = in.readUTF();
            Reducer<Object, Writable> reducer = new Reducer<>();
            reducer.readFields(in, conf);
            reducerMap.put(name, reducer);
        }

        int numBroadcast = in.readInt();
        for (int i = 0; i < numBroadcast; i++) {
            String name = in.readUTF();
            Writable value = WritableUtils.readWritableObject(in, conf);
            broadcastMap.put(name, value);
        }
    }
}