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

com.mongodb.flume.BucketedMongoDBSink Maven / Gradle / Ivy

The newest version!
package com.mongodb.flume;

import com.cloudera.flume.conf.Context;
import com.cloudera.flume.conf.SinkFactory.SinkBuilder;
import com.cloudera.flume.core.Event;
import com.cloudera.flume.core.EventSink;
import com.cloudera.util.Pair;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;


/**
 * Created by IntelliJ IDEA.
 * User: ayakushev
 * Date: 8/30/11
 * Time: 12:21 PM
 */
public class BucketedMongoDBSink extends EventSink.Base {

    static final Logger LOG = LoggerFactory.getLogger(BucketedMongoDBSink.class);

    private final SimpleLRUMap mongoWriters = new SimpleLRUMap(10,
            new SimpleLRUMap.OnRemove() {
                public void removed(MongoDBSink obj) {
                    try {
                        closeWriter(obj);
                    } catch (IOException e) {
                        LOG.warn("Failed to close weriter: " + obj._uri);
                    }
                }
            });

    private boolean shouldSub = false;
    private MongoDBSink singleWriter = null;
    private String formatUrl;

    public BucketedMongoDBSink(String formatUrl) {
        this.formatUrl = formatUrl;
        shouldSub = Event.containsTag(formatUrl);
    }

    public void append(Event e) throws IOException, InterruptedException {
        MongoDBSink w = singleWriter;
        if (shouldSub) {
            String realUrl = e.escapeString(formatUrl);
            w = mongoWriters.get(realUrl);
            if (w == null) {
                w = openWriter(realUrl);
                mongoWriters.put(realUrl, w);
            }
        }
        w.append(e);
        super.append(e);
    }

    protected MongoDBSink openWriter(String url) throws IOException {
        LOG.info("Opening " + url);
        MongoDBSink w = new MongoDBSink(url);
        w.open();
        return w;
    }

    @Override
    public void open() throws IOException {
        if (!shouldSub) {
            singleWriter = openWriter(formatUrl);
        }
    }

    protected void closeWriter(MongoDBSink writer) throws IOException {
        LOG.info("Closing writer " + writer._uri);
        writer.close();
    }

    @Override
    public void close() throws IOException {
        if (shouldSub) {
            mongoWriters.clear();
        } else {
            closeWriter(singleWriter);
            singleWriter = null;
        }
    }

    public static SinkBuilder builder() {
        return new SinkBuilder() {

            @Override
            public EventSink build(Context context, String... args) {
                Preconditions
                        .checkArgument(
                                args.length == 1,
                                "usage: mongoDBSink(\"mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]\")"
                                        + "\n ... See http://www.mongodb.org/display/DOCS/Connections for information on the MongoDB Connection URI Format."
                                        + "\n\t Note that using [?options] you can specify Write Concern related settings: "
                                        + "\n\t\t safe={true|false} (default: false) Whether or not the driver should send getLastError to verify each write operation."
                                        + "\n\t\t w={n} (default: 0) Specify the number of servers to replicate a write to before returning success. When non-zero, implies safe=true."
                                        + "\n\t\t wtimeout={ms} (default: wait forever) The number of milliseconds to wait for W replications to complete.  When non-zero, implies safe=true."
                                        + "\n\t\t fsync={true|false} (default: false) When enabled, forces an fsync after each write operation to increase durability.  You probably *don't* want to do this; see the MongoDB docs for info.  When 'true', implies safe=true");
                return new BucketedMongoDBSink(args[0]);
            }
        };
    }

    public static List> getSinkBuilders() {
        List> builders = new ArrayList>();
        builders.add(new Pair("bucketedMongoDBSink", builder()));
        return builders;
    }


    private static class SimpleLRUMap {

        private final Map> dataMap;
        private final int capacity;
        private final OnRemove removeCallback;

        public SimpleLRUMap(int capacity, OnRemove removeCallback) {
            this.dataMap = new HashMap>();
            this.capacity = capacity;
            this.removeCallback = removeCallback;
        }

        public V get(K key) {
            KeyValueWithUsage usageInfo = dataMap.get(key);
            if (usageInfo != null) {
                usageInfo.timestamp = System.currentTimeMillis();
                return usageInfo.value;
            }
            return null;
        }

        public V put(K key, V value) {
            KeyValueWithUsage oldValue = dataMap.put(key, new KeyValueWithUsage(key, value));
            if (dataMap.size() > capacity) {
                expireLRU();
            }
            return oldValue != null ? oldValue.value : null;
        }

        public V remove(K key) {
            KeyValueWithUsage removed = dataMap.remove(key);
            return onRemove(removed);
        }

        public void clear() {
            Iterator>> iter = dataMap.entrySet().iterator();
            while (iter.hasNext()) {
                KeyValueWithUsage removed = iter.next().getValue();
                iter.remove();
                onRemove(removed);
            }
        }

        private V onRemove(KeyValueWithUsage removed) {
            if (removed != null) {
                removeCallback.removed(removed.value);
                return removed.value;
            }
            return null;
        }

        private void expireLRU() {
            List> valueWithUsages = new ArrayList>(dataMap.values());
            Collections.sort(valueWithUsages);
            KeyValueWithUsage toExpire = valueWithUsages.get(0);
            remove(toExpire.key);
        }

        public static interface OnRemove {
            public void removed(T obj);
        }

        private static class KeyValueWithUsage implements Comparable {

            public K key;
            public V value;
            public long timestamp;

            public KeyValueWithUsage(K key, V value) {
                this.key = key;
                this.value = value;
                this.timestamp = System.currentTimeMillis();
            }

            public int compareTo(KeyValueWithUsage other) {
                return timestamp >= other.timestamp ? (timestamp != other.timestamp ? 1 : 0) : -1;
            }
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy