<commit_before>package com.skcraft.plume.common.service.claim;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.skcraft.plume.common.service.party.Party;
import com.skcraft.plume.common.service.party.PartyCache;
import com.skcraft.plume.common.util.WorldVector3i;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.map.hash.TLongObjectHashMap;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Loads claims asynchronously when they are requested, and then later
 * provides that information on demand.
 *
 * <p>Claims are loaded in batches whenever possible to minimize round trips
 * to the underlying database, which does mean that there may be a maximum
 * delay of one second (plus the time it takes for the database response)
 * for claim data that was requested to be made available. Claim data can
 * be requested on a chunk-by-chunk basis using
 * {@link #queueChunk(WorldVector3i)}, which should be called when
 * chunks are being loaded in a world. When chunks are being unloaded
 * from a world, then {@link #invalidateChunk(WorldVector3i)} should be
 * called.</p>
 *
 * <p>If changes are made to claims, then {@link #putClaims(Collection)}
 * can be used to inform the cache of updates. Only claims that have been
 * requested previously (through {@link #queueChunk(WorldVector3i)})
 * will be updated; claims not yet requested will not be cached.</p>
 *
 * <p>Multiple read attempts share a read lock and will minimally block,
 * but writes to the cache (through {@code putClaims()}) require a write
 * lock that will block other threads. However, the update is brief and
 * the block should not last for very long. Asynchronous updates (as
 * data is pulled from the underlying database) do not involve any locks.</p>
 *
 * <p>When the database is unable to retrieve claim data, the attempt
 * will be aborted and no claim data will be available for that chunk. This
 * behavior is subject to change in the future, with extra consideration
 * to prevent the queue from growing exceptionally large as errors
 * accumulate (such as in the case of complete database unavailability).</p>
 */
public class ClaimCache {

    /**
     * The default number of worker threads to use to load claim data
     * asynchronously.
     */
    public static final int DEFAULT_WORKER_COUNT = 2;

    private final LoadingCache<String, TLongObjectHashMap<ChunkState>> statesByWorld = CacheBuilder.newBuilder()
            .build(new CacheLoader<String, TLongObjectHashMap<ChunkState>>() {
                @Override
                public TLongObjectHashMap<ChunkState> load(@Nullable String key) throws Exception {
                    return new TLongObjectHashMap<>();
                }
            });

    private final ClaimMap claims;
    private final PartyCache parties;
    private final BlockingQueue<ChunkState> statePopulateQueue = new LinkedBlockingQueue<>();
    private final ReadWriteLock stateLock = new ReentrantReadWriteLock();

    /**
     * Create a new instance with {@code DEFAULT_WORKER_COUNT} worker threads.
     *
     * @param claims The underlying claim database
     * @param parties The party cache
     */
    public ClaimCache(ClaimMap claims, PartyCache parties) {
        this(claims, parties, DEFAULT_WORKER_COUNT);
    }

    /**
     * Create a new instance.
     *
     * @param claims The underlying claim database
     * @param parties The party cache
     * @param workerThreadCount The number of worker threads to use to load claims asynchronously
     */
    public ClaimCache(ClaimMap claims, PartyCache parties, int workerThreadCount) {
        checkNotNull(claims, "claims");
        checkNotNull(parties, "parties");
        checkArgument(workerThreadCount >= 1, "workerThreadCount >= 1");
        this.claims = claims;<commit_msg>Make ClaimMap gettable from ClaimCache.<commit_after>package com.skcraft.plume.common.service.claim;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.skcraft.plume.common.service.party.Party;
import com.skcraft.plume.common.service.party.PartyCache;
import com.skcraft.plume.common.util.WorldVector3i;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.map.hash.TLongObjectHashMap;
import lombok.Getter;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * Loads claims asynchronously when they are requested, and then later
 * provides that information on demand.
 *
 * <p>Claims are loaded in batches whenever possible to minimize round trips
 * to the underlying database, which does mean that there may be a maximum
 * delay of one second (plus the time it takes for the database response)
 * for claim data that was requested to be made available. Claim data can
 * be requested on a chunk-by-chunk basis using
 * {@link #queueChunk(WorldVector3i)}, which should be called when
 * chunks are being loaded in a world. When chunks are being unloaded
 * from a world, then {@link #invalidateChunk(WorldVector3i)} should be
 * called.</p>
 *
 * <p>If changes are made to claims, then {@link #putClaims(Collection)}
 * can be used to inform the cache of updates. Only claims that have been
 * requested previously (through {@link #queueChunk(WorldVector3i)})
 * will be updated; claims not yet requested will not be cached.</p>
 *
 * <p>Multiple read attempts share a read lock and will minimally block,
 * but writes to the cache (through {@code putClaims()}) require a write
 * lock that will block other threads. However, the update is brief and
 * the block should not last for very long. Asynchronous updates (as
 * data is pulled from the underlying database) do not involve any locks.</p>
 *
 * <p>When the database is unable to retrieve claim data, the attempt
 * will be aborted and no claim data will be available for that chunk. This
 * behavior is subject to change in the future, with extra consideration
 * to prevent the queue from growing exceptionally large as errors
 * accumulate (such as in the case of complete database unavailability).</p>
 */
public class ClaimCache {

    /**
     * The default number of worker threads to use to load claim data
     * asynchronously.
     */
    public static final int DEFAULT_WORKER_COUNT = 2;

    private final LoadingCache<String, TLongObjectHashMap<ChunkState>> statesByWorld = CacheBuilder.newBuilder()
            .build(new CacheLoader<String, TLongObjectHashMap<ChunkState>>() {
                @Override
                public TLongObjectHashMap<ChunkState> load(@Nullable String key) throws Exception {
                    return new TLongObjectHashMap<>();
                }
            });

    @Getter
    private final ClaimMap claimMap;
    private final PartyCache parties;
    private final BlockingQueue<ChunkState> statePopulateQueue = new LinkedBlockingQueue<>();
    private final ReadWriteLock stateLock = new ReentrantReadWriteLock();

    /**
     * Create a new instance with {@code DEFAULT_WORKER_COUNT} worker threads.
     *
     * @param claims The underlying claim database
     * @param parties The party cache
     */
    public ClaimCache(ClaimMap claims, PartyCache parties) {
        this(claims, parties, DEFAULT_WORKER_COUNT);
    }

    /**
     * Create a new instance.
     *
     * @param claims The underlying claim database
     * @param parties The party cache
     * @param workerThreadCount The number of worker threads to use to load claims asynchronously
     */
    public ClaimCache(ClaimMap claims, PartyCache parties, int workerThreadCount) {
        checkNotNull(claims, "claims");
        checkNotNull(parties, "parties");
        checkArgument(workerThreadCount >= 1, "workerThreadCount >= 1");
        this.claimMap = claims;
{'fraction_non_alphanumeric': 0.07946570070183383, 'fraction_numerical': 0.001584786053882726, 'mean_word_length': 3.913793103448276, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3110216', 'n_tokens_mistral': 2317, 'n_tokens_neox': 2250, 'n_words': 864}