Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to copy datastore entities between namespaces

I want to copy all the data in one namespace, say www.mysite.com, to another namespace, say nightly.latest.mysite.appspot.com. What's the best way to do this?

The example namespaces are not random: they're the namespaces that are set by a NamespaceFilter for the given domains that serve the app.

I want to be able to pull all the 'production' data into a 'non-production' namespace for testing.

like image 549
antony.trupe Avatar asked Dec 22 '22 21:12

antony.trupe


2 Answers

I'm using appengine-mapreduce for this. I won't walk through setting it up in detail. You can read the getting started guides for that information.

Right now you have to enter each class name to be copied. TODO is figuring out how to loop over all the __Stat_Kind__ results programmatically so each kind/class doesn't have to be specified separately.

import java.util.logging.Logger;

import org.apache.hadoop.io.NullWritable;

import com.google.appengine.api.NamespaceManager;
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.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.tools.mapreduce.AppEngineMapper;

public class DatastoreCopyMapper extends
        AppEngineMapper<Key, Entity, NullWritable, NullWritable> {

    private static final Logger log = Logger
            .getLogger(DatastoreCopyMapper.class.getName());
    private static String destination;

    public DatastoreCopyMapper() {
    }

    @Override
    public void taskSetup(Context context) {
        log.warning("Doing per-task setup");
        destination = context.getConfiguration().get("destination");
        log.warning("destination: " + destination);
    }

    @Override
    public void map(Key key, Entity value, Context context) {

        NamespaceManager.set(destination);
        String name = key.getName();
        long id = key.getId();
        Key destinationKey = null;
        if (name != null) {
            destinationKey = KeyFactory.createKey(key.getKind(), name);
        } else if (id != 0) {
            destinationKey = KeyFactory.createKey(key.getKind(), id);
        }
        Entity destinationEntity = new Entity(destinationKey);
        destinationEntity.setPropertiesFrom(value);
        DatastoreService datastore = DatastoreServiceFactory
                .getDatastoreService();

        datastore.put(destinationEntity);

    }
}

mapreduce.xml

<configurations>
  <configuration name="Copy between namespaces">
    <property>
      <name>mapreduce.map.class</name>
      <value>com.mysite.server.DatastoreCopyMapper</value>
    </property>
    <property>
      <name>mapreduce.inputformat.class</name>
      <value>com.google.appengine.tools.mapreduce.DatastoreInputFormat</value>
    </property>
    <property>
      <name human="Entity Kind to Map Over">mapreduce.mapper.inputformat.datastoreinputformat.entitykind</name>
      <value template="optional">User</value>
    </property>

    <property>
      <name human="Destination Namespace">mapreduce.mapper.inputformat.datastoreinputformat.destination</name>
      <value template="optional">dev.mysite.com</value>
    </property>

  </configuration>
</configurations>
like image 36
antony.trupe Avatar answered Dec 24 '22 09:12

antony.trupe


Namespace is the part of the Key. So you can't change or copy all data from one namespace to another. As I understand all you can do is to fetch all objects from one namespace and create NEW objects with the same properties in another namespace.

like image 59
Vladimir Prudnikov Avatar answered Dec 24 '22 11:12

Vladimir Prudnikov