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

com.turbospaces.ebean.AbstractEbeanCache Maven / Gradle / Ivy

There is a newer version: 2.0.33
Show newest version
package com.turbospaces.ebean;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;

import org.apache.commons.lang3.SerializationUtils;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.turbospaces.cache.BlockhoundCacheWrapper;
import com.turbospaces.cfg.ApplicationProperties;

import io.ebean.cache.ServerCacheConfig;
import io.ebean.cache.ServerCacheStatistics;
import io.ebean.cache.ServerCacheType;
import io.ebean.cache.TenantAwareKey;
import io.ebean.config.CurrentTenantProvider;
import io.ebeaninternal.server.cache.CachedBeanData;
import io.ebeaninternal.server.cache.CachedManyIds;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AbstractEbeanCache implements LocalCache, PutableCache {
    private final ApplicationProperties props;
    private final String cacheKey;
    private final BlockhoundCacheWrapper cache;
    private final TenantAwareKey tenantAwareKey;
    private final ServerCacheConfig config;

    protected AbstractEbeanCache(
            ApplicationProperties props,
            String cacheKey,
            BlockhoundCacheWrapper cache,
            CurrentTenantProvider tenantProvider,
            ServerCacheConfig config) {
        this.props = Objects.requireNonNull(props);
        this.cacheKey = Objects.requireNonNull(cacheKey);
        this.cache = Objects.requireNonNull(cache);
        this.tenantAwareKey = new TenantAwareKey(tenantProvider);
        this.config = Objects.requireNonNull(config);
    }
    @Override
    public Map getAll(Set keys) {
        BiMap transformed = HashBiMap.create();
        for (Object it : keys) {
            transformed.put(it, key(it));
        }

        ImmutableMap.Builder toReturn = ImmutableMap.builder();
        cache.getAllPresent(transformed.values()).forEach(new BiConsumer() {
            @Override
            public void accept(String k, Object v) {
                Object obj = transformed.inverse().get(k);
                toReturn.put(obj, v);
            }
        });

        return toReturn.build();
    }
    @Override
    public Object get(Object id) {
        String key = key(id);
        return cache.getIfPresent(key);
    }
    @Override
    public void put(Object id, Object obj) {
        String key = key(id);
        cache.put(key, obj);
    }
    @Override
    public void remove(Object id) {
        String key = key(id);
        cache.invalidate(key);
    }
    @Override
    public void clear() {
        cache.invalidateAll();
    }
    @Override
    public int size() {
        return (int) cache.size();
    }
    @Override
    public void cleanUp() {
        cache.cleanUp();
    }
    @Override
    public int hitRatio() {
        return (int) (cache.stats().hitRate() * 100);
    }
    @Override
    public ServerCacheStatistics statistics(boolean reset) {
        return LocalCache.stats(cacheKey, cache);
    }
    @Override
    public ServerCacheType type() {
        return config.getType();
    }
    @Override
    public String cacheKey() {
        return cacheKey;
    }
    @Override
    public String toString() {
        return cacheKey();
    }
    @Override
    public int hashCode() {
        return Objects.hash(cacheKey());
    }
    @Override
    public boolean equals(Object obj) {
        return Objects.equals(cacheKey, ((AbstractEbeanCache) obj).cacheKey());
    }
    @Override
    public void onRemove(byte[] data) {
        String key = SerializationUtils.deserialize(data);
        log.trace("onRemove {} by key: {}", cacheKey, key);
        cache.invalidate(key);
    }
    @Override
    public void onRemoveAll(byte[] data) throws Throwable {
        List keys = Lists.newArrayList();
        try (ByteArrayInputStream stream = new ByteArrayInputStream(data)) {
            try (ObjectInputStream in = new ObjectInputStream(stream)) {
                int size = in.readInt();
                for (int i = 0; i < size; i++) {
                    String key = in.readUTF();
                    keys.add(key);
                    cache.invalidate(key);
                }
            }
        }
        log.trace("onRemoveAll {} by keys: {}", cacheKey, keys);
    }
    @Override
    public int onClear() {
        String idx = props.CLOUD_APP_INSTANCE_INDEX.get();
        int size = size();
        log.debug("onClear {} items: {} on ({})", cacheKey, size, idx);
        cache.invalidateAll();
        return size;
    }
    @Override
    public void onPut(byte[] kbytes, byte[] vbytes) throws Throwable {
        String key = SerializationUtils.deserialize(kbytes);

        //
        // ~ fast and reusable input stream
        //
        switch (type()) {
            case NATURAL_KEY: {
                Object read = SerializationUtils.deserialize(vbytes);
                cache.put(key, read);
                break;
            }
            case BEAN: {
                try (ByteArrayInputStream in = new ByteArrayInputStream(vbytes)) {
                    try (ObjectInputStream ois = new ObjectInputStream(in)) {
                        CachedBeanData read = new CachedBeanData();
                        read.readExternal(ois);
                        onPut(key, read);
                    }
                }
                break;
            }
            case COLLECTION_IDS: {
                try (ByteArrayInputStream in = new ByteArrayInputStream(vbytes)) {
                    try (ObjectInputStream ois = new ObjectInputStream(in)) {
                        CachedManyIds read = new CachedManyIds();
                        read.readExternal(ois);
                        onPut(key, read);
                    }
                    break;
                }
            }
            default: {
                throw new IllegalArgumentException("unexpected cache type: " + type());
            }
        }
    }
    @Override
    public void onPutAll(byte[] data) throws Throwable {
        try (ByteArrayInputStream stream = new ByteArrayInputStream(data)) {
            try (ObjectInputStream in = new ObjectInputStream(stream)) {
                int size = in.readInt();
                for (int i = 0; i < size; i++) {
                    String key = in.readUTF();
                    switch (type()) {
                        case NATURAL_KEY: {
                            Object read = in.readObject();
                            cache.put(key, read);
                            break;
                        }
                        case BEAN: {
                            CachedBeanData read = new CachedBeanData();
                            read.readExternal(in);
                            onPut(key, read);
                            break;
                        }
                        case COLLECTION_IDS: {
                            CachedManyIds read = new CachedManyIds();
                            read.readExternal(in);
                            onPut(key, read);
                            break;
                        }
                        default: {
                            throw new IllegalArgumentException("unexpected cache type: " + type());
                        }
                    }
                }
            }
        }
    }
    protected String key(Object key) {
        return tenantAwareKey.key(key).toString();
    }
    protected void onPut(String key, CachedBeanData read) {
        //
        // ~ hack under strict concurrency (do not override the data which can be stale on other nodes)
        // ~ we will push the data back shortly
        //
        boolean toPut = true;
        CachedBeanData current = (CachedBeanData) cache.getIfPresent(key);
        if (Objects.nonNull(current)) {
            toPut = read.getVersion() > current.getVersion(); // ~ READ committed
        }

        if (toPut) {
            log.trace("onPut {} by key: {} bean value: {}", cacheKey, key, read);
            cache.put(key, read);
        }
    }
    protected void onPut(String key, CachedManyIds read) {
        log.trace("onPut {} by key: {} ids value: {}", cacheKey, key, read);
        cache.put(key, read);
    }
}