Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)



Datastore Best Practices

The Photofeed sample provides a useful technique in the general data model abstraction using interfaces whose implementation change depending on build time parameters, based on settings in build.properties . However, there are less obvious but important best practices that are used by Photofeed in the datastore-specific implementations.

How to do "Transactional Deletes" across Storages

In the Photofeed UI, there are no controls for deleting photos. This was done to keep the app as simple as possible. However, the source code does provide everything needed to delete photos. (The only part you need to add to the Photofeed app is a delete button that sets the Photo.isActive property to false.)

In the App Engine datastore implementation, the deletion of a photo requires deletions in two separate storages: the deletion of the Photo entity from Datastore and the deletion of the photo image blob from Google Cloud Storage. Both of these activities are launched in an App Engine cron job from the src.com.google.cloud.demo.CleanupCronServlet :

public class CleanupCronServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
    PhotoServiceManager manager = AppContext.getAppContext().getPhotoServiceManager();
    manager.cleanDeatctivedPhotos();
  }
}

Looking at the configuration file for this cron job at /war/WEB-INF/cron.xml , we see how this job is defined:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/cron/clean</url>
    <description>Clean up deleted photos</description>
    <schedule>every 5 minutes</schedule>
  </cron>
</cronentries>
The cron job invokes PhotoServiceManager.cleanDeactivatedPhotos every five minutes.

When this cron job runs, it gets a list of all Photo entities that have had their IsActive property set to false, in response to a delete button click made by the user in the app UI. This list is iterated with PhotoServiceManager.removeDeactivedPhoto invoked for each inactive photo:

  public void cleanDeatctivedPhotos() {
    Iterable photos = photoManager.getDeactivedPhotos();
    if (photos != null) {
      for (Photo photo : photos) {
        removeDeactivedPhoto(photo);
      }
    }

And here is what finally happens to each photo

  private void removeDeactivedPhoto(Photo photo) {
    if (photo != null && !photo.isActive()) {
      try {
        FileService fileService = FileServiceFactory.getFileService();
        BlobKey blobKey = photo.getBlobKey();
        AppEngineFile file = fileService.getBlobFile(blobKey);
        FileStat stat = fileService.stat(file);
        if (stat != null) {
          logger.fine("photo:" + photo.getId() + " blob file stat is not null");
          BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
          blobstoreService.delete(blobKey);
          logger.info("The blob is deleted. try to delete the entity from datastore.");
          photoManager.deleteEntity(photo);
        }
      } catch (FileNotFoundException e) {
        logger.info("The blob is alrady deleted. try to delete the entity from datastore.");
        photoManager.deleteEntity(photo);
      } catch (Exception e) {
        logger.severe("Failed to delete the blob storge for photo " + photo.getId() +
            ":" + e.getMessage());
      }

Notice that the blob is deleted first from Google Cloud Storage using the blobstoreService . If you delete the Photo object from the Datastore first, you won't be able to delete the blob because you'll have lost the blob key. After the blob is deleted, the corresponding Photo entity is deleted.

Notice that although the cron job runs every five minutes, it does not mean the UI will show stale results. Remember that in photofeed.jsp only active Photo entities are retrieved and used to serve photo blobs. So a delete that sets the IsActive property to false has an immediate UI effect, even though the actual physical deletion happens later.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.