com.redhat.lightblue.mongo.crud.BasicDocDeleter.java Source code

Java tutorial

Introduction

Here is the source code for com.redhat.lightblue.mongo.crud.BasicDocDeleter.java

Source

/*
 Copyright 2013 Red Hat, Inc. and/or its affiliates.
    
 This file is part of lightblue.
    
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
    
 This program 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 General Public License for more details.
    
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.redhat.lightblue.mongo.crud;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.MongoException;
import com.mongodb.BasicDBObject;
import com.mongodb.BulkWriteError;
import com.mongodb.BulkWriteException;
import com.mongodb.BulkWriteOperation;
import com.mongodb.BulkWriteResult;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;
import com.mongodb.ReadPreference;
import com.redhat.lightblue.crud.ListDocumentStream;
import com.redhat.lightblue.crud.CRUDDeleteResponse;
import com.redhat.lightblue.crud.CRUDOperation;
import com.redhat.lightblue.crud.CRUDOperationContext;
import com.redhat.lightblue.crud.DocCtx;
import com.redhat.lightblue.interceptor.InterceptPoint;
import com.redhat.lightblue.util.Error;

/**
 * Basic document deletion with no transaction support
 */
public class BasicDocDeleter implements DocDeleter {

    private static final Logger LOGGER = LoggerFactory.getLogger(BasicDocDeleter.class);

    // TODO: move this to a better place
    public final int batchSize;

    // Use in tests. Set to false to test deletion with bulk operations
    public boolean hookOptimization = true;

    private final DocTranslator translator;
    private final WriteConcern writeConcern;

    public BasicDocDeleter(DocTranslator translator, WriteConcern writeConcern, int batchSize) {
        super();
        this.translator = translator;
        this.writeConcern = writeConcern;
        this.batchSize = batchSize;
    }

    @Override
    public void delete(CRUDOperationContext ctx, DBCollection collection, DBObject mongoQuery,
            CRUDDeleteResponse response) {
        LOGGER.debug("Removing docs with {}", mongoQuery);

        int numDeleted = 0;

        if (!hookOptimization || ctx.getHookManager().hasHooks(ctx, CRUDOperation.DELETE)) {
            LOGGER.debug("There are hooks, retrieve-delete");
            try (DBCursor cursor = collection.find(mongoQuery, null)) {
                // Set read preference to primary for read-for-update operations
                cursor.setReadPreference(ReadPreference.primary());

                // All docs, to be put into the context
                ArrayList<DocCtx> contextDocs = new ArrayList<>();
                // ids to delete from the db
                List<Object> idsToDelete = new ArrayList<>(batchSize);
                while (cursor.hasNext()) {

                    // We will use this index to access the documents deleted in this batch
                    int thisBatchIndex = contextDocs.size();
                    if (idsToDelete.size() < batchSize) {
                        // build batch
                        DBObject doc = cursor.next();
                        DocTranslator.TranslatedDoc tdoc = translator.toJson(doc);
                        DocCtx docCtx = new DocCtx(tdoc.doc, tdoc.rmd);
                        docCtx.setOriginalDocument(docCtx);
                        docCtx.setCRUDOperationPerformed(CRUDOperation.DELETE);
                        contextDocs.add(docCtx);
                        idsToDelete.add(doc.get(MongoCRUDController.ID_STR));
                    }

                    if (idsToDelete.size() == batchSize || !cursor.hasNext()) {
                        // batch built or run out of documents                        
                        BulkWriteOperation bw = collection.initializeUnorderedBulkOperation();

                        for (Object id : idsToDelete) {
                            // doing a bulk of single operations instead of removing by initial query
                            // that way we know which documents were not removed
                            bw.find(new BasicDBObject("_id", id)).remove();
                        }

                        BulkWriteResult result = null;
                        try {
                            if (writeConcern == null) {
                                LOGGER.debug("Bulk deleting docs");
                                result = bw.execute();
                            } else {
                                LOGGER.debug("Bulk deleting docs with writeConcern={} from execution",
                                        writeConcern);
                                result = bw.execute(writeConcern);
                            }
                            LOGGER.debug("Bulk deleted docs - attempted {}, deleted {}", idsToDelete.size(),
                                    result.getRemovedCount());
                        } catch (BulkWriteException bwe) {
                            LOGGER.error("Bulk write exception", bwe);
                            handleBulkWriteError(bwe.getWriteErrors(),
                                    contextDocs.subList(thisBatchIndex, contextDocs.size()));
                            result = bwe.getWriteResult();
                        } catch (RuntimeException e) {
                            LOGGER.error("Exception", e);
                            throw e;
                        } finally {

                            numDeleted += result.getRemovedCount();
                            // clear list before processing next batch
                            idsToDelete.clear();
                        }
                    }
                }
                ctx.setDocumentStream(new ListDocumentStream<DocCtx>(contextDocs));
            }
        } else {
            LOGGER.debug("There are no hooks, deleting in bulk");
            try {
                if (writeConcern == null) {
                    numDeleted = collection.remove(mongoQuery).getN();
                } else {
                    numDeleted = collection.remove(mongoQuery, writeConcern).getN();
                }
            } catch (MongoException e) {
                LOGGER.error("Deletion error", e);
                throw e;
            }
            ctx.setDocumentStream(new ListDocumentStream<DocCtx>(new ArrayList<DocCtx>()));
        }

        response.setNumDeleted(numDeleted);
    }

    private void handleBulkWriteError(List<BulkWriteError> errors, List<DocCtx> docs) {
        for (BulkWriteError e : errors) {
            DocCtx doc = docs.get(e.getIndex());
            doc.addError(Error.get("remove", MongoCrudConstants.ERR_DELETE_ERROR, e.getMessage()));
        }
    }
}