All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.yawp.driver.appengine.pipes.flow.JoinTask Maven / Gradle / Ivy

package io.yawp.driver.appengine.pipes.flow;

import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.appengine.api.taskqueue.DeferredTask;
import io.yawp.repository.IdRef;
import io.yawp.repository.Repository;
import io.yawp.repository.models.ObjectHolder;
import io.yawp.repository.pipes.SinkMarker;
import io.yawp.repository.pipes.SourceMarker;
import io.yawp.repository.query.NoResultException;

import java.util.*;
import java.util.logging.Logger;

import static io.yawp.driver.appengine.pipes.flow.CacheHelper.POW_2_15;
import static io.yawp.repository.Yawp.yawp;

public class JoinTask implements DeferredTask {

    private final static Logger logger = Logger.getLogger(JoinTask.class.getName());

    private static final int BUSY_WAIT_TIMES = 20;

    private static final int BUSY_WAIT_SLEEP = 250;

    private String ns;

    private String sinkGroupUri;

    private Integer index;

    private transient Repository r;

    private transient String indexCacheKey;

    private transient String lockCacheKey;

    private transient String indexHash;

    private transient MemcacheService memcache;

    private transient Map, Object> sinkCache;

    private transient Set> sinksToSave;

    private transient Map, SinkMarker> sinkMarkerCache;

    private transient Set> sinkMarkersToSave;

    public JoinTask(String ns, String sinkGroupUri, Integer index) {
        this.ns = ns;
        this.sinkGroupUri = sinkGroupUri;
        this.index = index;
    }

    @Override
    public void run() {
        init();
        log();
        join();
    }

    private void init() {
        this.r = yawp().namespace(ns);
        this.memcache = MemcacheServiceFactory.getMemcacheService();
        this.indexCacheKey = CacheHelper.createIndexCacheKey(sinkGroupUri);
        this.lockCacheKey = CacheHelper.createLockCacheKey(sinkGroupUri, index);
        this.indexHash = CacheHelper.createIndexHash(sinkGroupUri, index);
        this.sinkCache = new HashMap<>();
        this.sinksToSave = new HashSet<>();
        this.sinkMarkerCache = new HashMap<>();
        this.sinkMarkersToSave = new HashSet<>();
    }

    private void log() {
        logger.info(String.format("join-task - sinkGroupId: %s", sinkGroupUri));
    }

    private void join() {
        memcache.increment(indexCacheKey, 1);
        memcache.increment(lockCacheKey, -1 * POW_2_15);

        busyWaitForWriters(BUSY_WAIT_TIMES, BUSY_WAIT_SLEEP);
        execute();
    }

    private void execute() {
        List works = listWorks();

        try {
            r.begin();
            for (Work work : works) {
                executeIfLastVersion(work);
            }
            commitIfChanged();
        } finally {
            if (r.isTransationInProgress()) {
                r.rollback();
            }
        }

        destroyWorks(works);
    }

    private void destroyWorks(List works) {
        for (Work work : works) {
            r.destroy(work.getId());
        }
    }

    private void commitIfChanged() {
        if (sinksToSave.isEmpty()) {
            r.rollback();
            return;
        }

        for (IdRef id : sinkMarkersToSave) {
            r.save(sinkMarkerCache.get(id));
        }

        for (IdRef sinkId : sinksToSave) {
            logSave(sinkId);
            r.save(sinkCache.get(sinkId));
        }

        // TODO: destroy empty sinks?
        r.commit();
    }

    private void logSave(IdRef sinkId) {
        logger.info(String.format("join-task - saving sinkId: %s", sinkId.getUri()));
    }

    private void executeIfLastVersion(Work work) {

        SourceMarker sourceVersion = work.getSourceMarker();

        IdRef sinkMarkerId = work.createSinkMarkerId();
        SinkMarker sinkMarker = getFromCacheOrFetchSinkMarker(sinkMarkerId);

        if (sinkMarker.getVersion() >= work.getSourceVersion()) {
            return;
        }

        Object sink = getFromCacheOrFetchOrCreateSink(work.getSinkId());
        work.execute(sink, sinkMarker);

        sinksToSave.add(work.getSinkId());
        sinkMarkerCache.put(sinkMarkerId, sinkMarker);
        sinkMarkersToSave.add(sinkMarkerId);
    }

    private Object getFromCacheOrFetchOrCreateSink(IdRef sinkId) {
        if (sinkCache.containsKey(sinkId)) {
            return sinkCache.get(sinkId);
        }

        Object sink = fetchOrCreateSink(sinkId);
        sinkCache.put(sinkId, sink);
        return sink;
    }

    private Object fetchOrCreateSink(IdRef sinkId) {
        try {
            return sinkId.fetch();
        } catch (NoResultException e) {
            try {
                Object sink = sinkId.getClazz().newInstance();
                ObjectHolder objectHolder = new ObjectHolder(sink);
                objectHolder.setId(sinkId);
                if (sinkId.getParentClazz() != null) {
                    objectHolder.setParentId(sinkId.getParentId());
                }
                return sink;
            } catch (InstantiationException | IllegalAccessException e1) {
                throw new RuntimeException(e);
            }
        }
    }

    private SinkMarker getFromCacheOrFetchSinkMarker(IdRef sinkMarkerId) {
        if (sinkMarkerCache.containsKey(sinkMarkerId)) {
            return sinkMarkerCache.get(sinkMarkerId);
        }

        SinkMarker sinkMarker = fetchOrCreateSinkMarker(sinkMarkerId);
        sinkMarkerCache.put(sinkMarkerId, sinkMarker);
        return sinkMarker;
    }

    private SinkMarker fetchOrCreateSinkMarker(IdRef sinkMarkerId) {
        try {
            return sinkMarkerId.fetch();
        } catch (NoResultException e) {
            SinkMarker sinkMarker = new SinkMarker();
            sinkMarker.setId(sinkMarkerId);
            sinkMarker.setParentId(sinkMarkerId.getParentId());
            sinkMarker.setVersion(0L);
            sinkMarker.setPresent(false);
            return sinkMarker;
        }
    }

    private List listWorks() {
        return r.query(Work.class).where("indexHash", "=", indexHash).order("id").list();
    }

    private void busyWaitForWriters(int times, int sleep) {
        for (int i = 0; i < times; i++) {
            Long counter = (Long) memcache.get(lockCacheKey);
            if (counter == null || counter < POW_2_15) {
                break;
            }
            try {
                Thread.sleep(sleep);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy