com.torodb.torod.db.executor.jobs.InsertCallable.java Source code

Java tutorial

Introduction

Here is the source code for com.torodb.torod.db.executor.jobs.InsertCallable.java

Source

/*
 *     This file is part of ToroDB.
 *
 *     ToroDB 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.
 *
 *     ToroDB 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 ToroDB. If not, see <http://www.gnu.org/licenses/>.
 *
 *     Copyright (c) 2014, 8Kdata Technology
 *     
 */

package com.torodb.torod.db.executor.jobs;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.torodb.torod.core.WriteFailMode;
import com.torodb.torod.core.connection.InsertResponse;
import com.torodb.torod.core.connection.WriteError;
import com.torodb.torod.core.dbWrapper.DbConnection;
import com.torodb.torod.core.dbWrapper.exceptions.DbException;
import com.torodb.torod.core.dbWrapper.exceptions.ImplementationDbException;
import com.torodb.torod.core.dbWrapper.exceptions.UserDbException;
import com.torodb.torod.core.exceptions.ToroException;
import com.torodb.torod.core.exceptions.ToroImplementationException;
import com.torodb.torod.core.exceptions.ToroRuntimeException;
import com.torodb.torod.core.subdocument.SplitDocument;
import com.torodb.torod.core.subdocument.SubDocType;
import com.torodb.torod.core.subdocument.SubDocument;
import java.util.*;
import javax.annotation.Nonnull;

/**
 *
 */
public class InsertCallable extends TransactionalJob<InsertResponse> {

    private final String collection;
    private final Collection<SplitDocument> docs;
    private final WriteFailMode mode;
    private final Report report;

    public InsertCallable(DbConnection connection, TransactionAborter abortCallback, Report report,
            String collection, Collection<SplitDocument> docs, WriteFailMode mode) {
        super(connection, abortCallback);
        this.collection = collection;
        this.docs = docs;
        this.mode = mode;
        this.report = report;
    }

    @Override
    protected InsertResponse failableCall() throws ToroException, ToroRuntimeException {
        try {
            InsertResponse result;
            switch (mode) {
            case ISOLATED:
                result = isolatedInsert();
                break;
            case ORDERED:
                result = orderedInsert();
                break;
            case TRANSACTIONAL:
                result = transactionalInsert();
                break;
            default:
                throw new AssertionError("Study exceptions");
            }
            report.insertExecuted(collection, docs, mode, result);

            return result;
        } catch (ImplementationDbException ex) {
            throw new ToroImplementationException(ex);
        }
    }

    private void insertSingleDoc(SplitDocument doc) throws ImplementationDbException, UserDbException {
        DbConnection connection = getConnection();
        connection.insertRootDocuments(collection, Collections.singleton(doc));

        for (SubDocType type : doc.getSubDocuments().rowKeySet()) {
            ImmutableCollection<SubDocument> subDocs = doc.getSubDocuments().row(type).values();

            connection.insertSubdocuments(collection, type, subDocs.iterator());
        }
    }

    private InsertResponse isolatedInsert() throws ImplementationDbException {
        int index = 0;
        List<WriteError> errors = Lists.newLinkedList();
        for (SplitDocument doc : docs) {
            try {
                insertSingleDoc(doc);

                index++;
            } catch (UserDbException ex) {
                appendError(errors, ex, index);
            }
        }

        return createResponse(docs.size(), errors);
    }

    private InsertResponse orderedInsert() throws ImplementationDbException {
        int index = 0;
        List<WriteError> errors = Lists.newLinkedList();
        try {
            for (SplitDocument doc : docs) {
                insertSingleDoc(doc);

                index++;
            }
        } catch (UserDbException ex) {
            appendError(errors, ex, index);
        }

        return createResponse(docs.size(), errors);
    }

    private InsertResponse transactionalInsert() throws ImplementationDbException {
        DbConnection connection = getConnection();
        List<WriteError> errors = Lists.newLinkedList();

        try {
            /*
            * First we need to store the root documents
            */
            connection.insertRootDocuments(collection, docs);

            /*
            * Then we have to store the subdocuments. It is more efficient to do one insert for each table, so inserts
            * are done by subdocument type.
            * To do that, we could create a map like Map<SubDocType, List<SubDocument>> and then iterate over the keys,
            * but we need to duplicate memory and the documents to insert may be very big. So we decided to do it in a
            * functional way. First we get all types and then we use an iterator that, for each type 't' and document 'd'
            * does d.getSubDocuments().row(k).values().iterator and finally merges the iterators grouped by type.
            */
            Set<SubDocType> types = Sets.newHashSetWithExpectedSize(10 * docs.size());
            for (SplitDocument splitDocument : docs) {
                types.addAll(splitDocument.getSubDocuments().rowKeySet());
            }

            /*
            * The following code that uses guava functions is the same as the following jdk8 code:
            * for (SubDocType type : types) {
            *   java.util.function.Function<SplitDocument, Stream<SubDocument>> f = (sd) -> sd.getSubDocuments().row(type).values().stream();
            *
            *   Stream<SubDocument> flatMap = docs.stream().map(f).flatMap((stream) -> stream);
            *
            *   connection.insertSubdocuments(collection, type, flatMap.iterator());
            *
            * }
            */
            for (SubDocType type : types) {
                Function<SplitDocument, Iterable<SubDocument>> extractor = new SubDocumentExtractorFunction(type);

                connection.insertSubdocuments(collection, type,
                        Iterables.concat(Iterables.transform(docs, extractor)).iterator());
            }

            return createResponse(docs.size(), errors);
        } catch (UserDbException ex) {
            appendError(errors, ex, 0);
            connection.rollback();
        }
        return createResponse(0, errors);
    }

    private InsertResponse createResponse(int docInserted, @Nonnull List<WriteError> errors) {
        if (errors.isEmpty()) {
            assert docInserted == docs.size();
            return new InsertResponse(true, docInserted, null);
        }
        return new InsertResponse(false, docInserted, ImmutableList.copyOf(errors));
    }

    private void appendError(@Nonnull List<WriteError> errors, DbException ex, int index) {
        errors.add(new WriteError(index, getErrorCode(ex), getErrorMessage(ex)));
    }

    private int getErrorCode(DbException ex) {
        return -1;
    }

    private String getErrorMessage(DbException ex) {
        return ex.getMessage();
    }

    /**
     * This function extracts the subdocuments of a given type contained in a given
     * {@linkplain SubDocument subdocuments}.
     */
    private static class SubDocumentExtractorFunction implements Function<SplitDocument, Iterable<SubDocument>> {

        private final SubDocType type;

        public SubDocumentExtractorFunction(SubDocType type) {
            this.type = type;
        }

        @Override
        public Iterable<SubDocument> apply(SplitDocument input) {
            if (input == null) {
                throw new IllegalArgumentException();
            }
            return input.getSubDocuments().row(type).values();
        }
    }

    public static interface Report {
        public void insertExecuted(String collection, Collection<SplitDocument> docs, WriteFailMode mode,
                InsertResponse response);
    }
}