/*
* Copyright 2010 Google Inc.
*
* Licensed 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 com.google.appengine.tools.mapreduce.impl;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.tools.mapreduce.DatastoreMutationPool;
import java.util.ArrayList;
import java.util.Collection;
/**
* DatastoreMutationPoolImpl allows you to pool datastore operations such that
* they are applied in batches requiring fewer datastore API calls. Mutations
* are accumulated until they reach a count limit on the number of unflushed
* mutations, until they reach a size limit on the byte count of unflushed
* mutations, or until a manual flush is requested.
*
*/
public class DatastoreMutationPoolImpl extends DatastoreMutationPool {
// --------------------------- STATIC FIELDS ---------------------------
/**
* Default number of operations to batch before automatically flushing
*/
public static final int DEFAULT_COUNT_LIMIT = 100;
/**
* Default size (in bytes) of operations to batch before automatically
* flushing.
*
* <p>The current value is 256 KB.
*/
public static final int DEFAULT_SIZE_LIMIT = 1 << 18;
// ------------------------------ FIELDS ------------------------------
private final DatastoreService ds;
private final int countLimit;
private final int sizeLimit;
private final Collection<Entity> puts = new ArrayList<Entity>();
private int putsSize = 0;
private final Collection<Key> deletes = new ArrayList<Key>();
private int deletesSize = 0;
// --------------------------- CONSTRUCTORS ---------------------------
@SuppressWarnings("UnusedDeclaration")
public DatastoreMutationPoolImpl() {
this(DatastoreServiceFactory.getDatastoreService(), DEFAULT_COUNT_LIMIT, DEFAULT_SIZE_LIMIT);
}
DatastoreMutationPoolImpl(DatastoreService ds, int countLimit, int sizeLimit) {
this.ds = ds;
this.countLimit = countLimit;
this.sizeLimit = sizeLimit;
}
// ------------------------ IMPLEMENTING METHODS ------------------------
@Override
public void delete(Key key) {
// This is probably a serious overestimation, but I can't see a good
// way to find the size in the public API.
int deleteSize = KeyFactory.keyToString(key).length();
// Do this before the add so that we guarantee that size is never > sizeLimit
if (deletesSize + deleteSize >= sizeLimit) {
flushDeletes();
}
deletesSize += deleteSize;
deletes.add(key);
if (deletes.size() >= countLimit) {
flushDeletes();
}
}
@Override
public void flush() {
if (!puts.isEmpty()) {
flushPuts();
}
if (!deletes.isEmpty()) {
flushDeletes();
}
}
@Override
public void put(Entity entity) {
int putSize = EntityTranslator.convertToPb(entity).getSerializedSize();
// Do this before the add so that we guarantee that size is never > sizeLimit
if (putsSize + putSize >= sizeLimit) {
flushPuts();
}
putsSize += putSize;
puts.add(entity);
if (puts.size() >= countLimit) {
flushPuts();
}
}
// -------------------------- INSTANCE METHODS --------------------------
private void flushDeletes() {
ds.delete(deletes);
deletes.clear();
deletesSize = 0;
}
private void flushPuts() {
ds.put(puts);
puts.clear();
putsSize = 0;
}
}