Package de.fhg.igd.mongomvcc.impl

Source Code of de.fhg.igd.mongomvcc.impl.MongoDBVMaintenance

// This file is part of MongoMVCC.
//
// Copyright (c) 2012 Fraunhofer IGD
//
// MongoMVCC is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// MongoMVCC 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with MongoMVCC. If not, see <http://www.gnu.org/licenses/>.

package de.fhg.igd.mongomvcc.impl;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

import de.fhg.igd.mongomvcc.VHistory;
import de.fhg.igd.mongomvcc.VMaintenance;
import de.fhg.igd.mongomvcc.helper.IdHashSet;
import de.fhg.igd.mongomvcc.helper.IdMap;
import de.fhg.igd.mongomvcc.helper.IdMapIterator;
import de.fhg.igd.mongomvcc.helper.IdSet;
import de.fhg.igd.mongomvcc.impl.internal.Commit;
import de.fhg.igd.mongomvcc.impl.internal.MongoDBConstants;
import de.fhg.igd.mongomvcc.impl.internal.Tree;

/**
* MongoDB implementation of MVCC database maintenance operations. Locks
* the database during these operations. This might not be enough if some
* concurrent thread/process is just in the progress of creating a new commit,
* but it's better than nothing.
* @author Michel Kraemer
*/
public class MongoDBVMaintenance implements VMaintenance {
  /**
   * The database
   */
  private final MongoDBVDatabase _db;
 
  /**
   * Constructs the maintenance object
   * @param db the database
   */
  public MongoDBVMaintenance(MongoDBVDatabase db) {
    _db = db;
  }
 
  @Override
  public long[] findDanglingCommits(long expiry, TimeUnit unit) {
    _db.getDB().getMongo().fsyncAndLock();
    try {
      return doFindDanglingCommits(expiry, unit);
    } finally {
      _db.getDB().getMongo().unlock();
    }
  }
 
  @Override
  public long pruneDanglingCommits(long expiry, TimeUnit unit) {
    long[] cids = findDanglingCommits(expiry, unit);
    DBCollection collCommits = _db.getDB().getCollection(MongoDBConstants.COLLECTION_COMMITS);
   
    //delete commits in chunks, so we avoid sending an array that is
    //larger than the maximum document size
    final int sliceCount = 1000;
    for (int i = 0; i < cids.length; i += sliceCount) {
      int maxSliceCount = Math.min(sliceCount, cids.length - i);
      long[] slice = new long[maxSliceCount];
      System.arraycopy(cids, i, slice, 0, maxSliceCount);
      collCommits.remove(new BasicDBObject(MongoDBConstants.ID,
          new BasicDBObject("$in", slice)));
    }
   
    return cids.length;
  }
 
  private long getMaxTime(long expiry, TimeUnit unit) {
    long expiryMillis = unit.toMillis(expiry);
    long currentTime = System.currentTimeMillis();
    long maxTime = currentTime - expiryMillis;
    return maxTime;
  }
 
  private long[] doFindDanglingCommits(long expiry, TimeUnit unit) {
    long maxTime = getMaxTime(expiry, unit);
   
    //load all commits which are older than the expiry time. mark them as dangling
    DBCollection collCommits = _db.getDB().getCollection(MongoDBConstants.COLLECTION_COMMITS);
    DBCursor commits = collCommits.find(new BasicDBObject(MongoDBConstants.TIMESTAMP,
        new BasicDBObject("$not", new BasicDBObject("$gte", maxTime))), //also include commits without a timestamp
        new BasicDBObject(MongoDBConstants.ID, 1));
    IdSet danglingCommits = new IdHashSet();
    for (DBObject o : commits) {
      long cid = (Long)o.get(MongoDBConstants.ID);
      danglingCommits.add(cid);
    }
   
    //walk through all branches and eliminate commits which are not dangling
    DBCollection collBranches = _db.getDB().getCollection(MongoDBConstants.COLLECTION_BRANCHES);
    DBCursor branches = collBranches.find(new BasicDBObject(), new BasicDBObject(MongoDBConstants.CID, 1));
    VHistory history = _db.getHistory();
    IdSet alreadyCheckedCommits = new IdHashSet();
    for (DBObject o : branches) {
      long cid = (Long)o.get(MongoDBConstants.CID);
      while (cid != 0) {
        if (alreadyCheckedCommits.contains(cid)) {
          break;
        }
        alreadyCheckedCommits.add(cid);
        danglingCommits.remove(cid);
        cid = history.getParent(cid);
      }
    }
   
    //all remaining commits must be dangling
    return danglingCommits.toArray();
  }

  @Override
  public long[] findUnreferencedDocuments(String collection, long expiry,
      TimeUnit unit) {
    _db.getDB().getMongo().fsyncAndLock();
    try {
      return doFindUnreferencedDocuments(collection, expiry, unit);
    } finally {
      _db.getDB().getMongo().unlock();
    }
  }
 
  @Override
  public long pruneUnreferencedDocuments(String collection, long expiry,
      TimeUnit unit) {
    long[] oids = findUnreferencedDocuments(collection, expiry, unit);
    DBCollection coll = _db.getDB().getCollection(collection);
   
    //delete documents in chunks, so we avoid sending an array that is
    //larger than the maximum document size
    final int sliceCount = 1000;
    for (int i = 0; i < oids.length; i += sliceCount) {
      int maxSliceCount = Math.min(sliceCount, oids.length - i);
      long[] slice = new long[maxSliceCount];
      System.arraycopy(oids, i, slice, 0, maxSliceCount);
      coll.remove(new BasicDBObject(MongoDBConstants.ID,
          new BasicDBObject("$in", slice)));
    }
   
    return oids.length;
  }
 
  private long[] doFindUnreferencedDocuments(String collection, long expiry,
      TimeUnit unit) {
    long maxTime = getMaxTime(expiry, unit);
   
    //fetch the OIDs of all documents older than the expiry time
    DBCollection collDocs = _db.getDB().getCollection(collection);
    DBCursor docs = collDocs.find(new BasicDBObject(MongoDBConstants.TIMESTAMP,
        new BasicDBObject("$not", new BasicDBObject("$gte", maxTime))), //also include docs without a timestamp
        new BasicDBObject(MongoDBConstants.ID, 1));
    IdSet oids = new IdHashSet(docs.count());
    for (DBObject o : docs) {
      oids.add((Long)o.get(MongoDBConstants.ID));
    }
   
    //iterate through all commits and eliminate referenced documents
    DBCollection collCommits = _db.getDB().getCollection(MongoDBConstants.COLLECTION_COMMITS);
    for (DBObject o : collCommits.find()) {
      Commit c = Tree.deserializeCommit(o);
      Map<String, IdMap> allObjs = c.getObjects();
      IdMap objs = allObjs.get(collection);
      if (objs != null) {
        //eliminate OIDs referenced by this commit
        IdMapIterator mi = objs.iterator();
        while (mi.hasNext()) {
          mi.advance();
          oids.remove(mi.value());
        }
      }
    }
   
    //the remaining OIDs must be the unreferenced ones
    return oids.toArray();
  }
}
TOP

Related Classes of de.fhg.igd.mongomvcc.impl.MongoDBVMaintenance

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.