/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ignite.internal.processors.cache.database.evict; import java.util.concurrent.ThreadLocalRandom; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.configuration.MemoryConfiguration; import org.apache.ignite.configuration.MemoryPolicyConfiguration; import org.apache.ignite.internal.pagemem.PageIdUtils; import org.apache.ignite.internal.pagemem.impl.PageMemoryNoStoreImpl; import org.apache.ignite.internal.processors.cache.GridCacheSharedContext; import org.apache.ignite.internal.util.GridUnsafe; import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.U; /** * */ public class Random2LruPageEvictionTracker extends PageAbstractEvictionTracker { /** Evict attempts limit. */ private static final int EVICT_ATTEMPTS_LIMIT = 30; /** LRU Sample size. */ private static final int SAMPLE_SIZE = 5; /** Maximum sample search spin count */ private static final int SAMPLE_SPIN_LIMIT = SAMPLE_SIZE * 1000; /** Logger. */ private final IgniteLogger log; /** Tracking array ptr. */ private long trackingArrPtr; /** * @param pageMem Page memory. * @param plcCfg Policy config. * @param sharedCtx Shared context. */ public Random2LruPageEvictionTracker( PageMemoryNoStoreImpl pageMem, MemoryPolicyConfiguration plcCfg, GridCacheSharedContext<?, ?> sharedCtx ) { super(pageMem, plcCfg, sharedCtx); MemoryConfiguration memCfg = sharedCtx.kernalContext().config().getMemoryConfiguration(); assert plcCfg.getMaxSize() / memCfg.getPageSize() < Integer.MAX_VALUE; log = sharedCtx.logger(getClass()); } /** {@inheritDoc} */ @Override public void start() throws IgniteException { trackingArrPtr = GridUnsafe.allocateMemory(trackingSize * 8); GridUnsafe.setMemory(trackingArrPtr, trackingSize * 8, (byte)0); } /** {@inheritDoc} */ @Override public void stop() throws IgniteException { GridUnsafe.freeMemory(trackingArrPtr); } /** {@inheritDoc} */ @Override public void touchPage(long pageId) throws IgniteCheckedException { int pageIdx = PageIdUtils.pageIndex(pageId); long latestTs = compactTimestamp(U.currentTimeMillis()); assert latestTs >= 0 && latestTs < Integer.MAX_VALUE; boolean success; do { int trackingIdx = trackingIdx(pageIdx); int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8); int secondTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8 + 4); if (firstTs <= secondTs) success = GridUnsafe.compareAndSwapInt(null, trackingArrPtr + trackingIdx * 8, firstTs, (int)latestTs); else { success = GridUnsafe.compareAndSwapInt( null, trackingArrPtr + trackingIdx * 8 + 4, secondTs, (int)latestTs); } } while (!success); } /** {@inheritDoc} */ @Override public void evictDataPage() throws IgniteCheckedException { ThreadLocalRandom rnd = ThreadLocalRandom.current(); int evictAttemptsCnt = 0; while (evictAttemptsCnt < EVICT_ATTEMPTS_LIMIT) { int lruTrackingIdx = -1; int lruCompactTs = Integer.MAX_VALUE; int dataPagesCnt = 0; int sampleSpinCnt = 0; while (dataPagesCnt < SAMPLE_SIZE) { int trackingIdx = rnd.nextInt(trackingSize); int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8); int secondTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8 + 4); int minTs = Math.min(firstTs, secondTs); int maxTs = Math.max(firstTs, secondTs); if (maxTs != 0) { // We chose data page with at least one touch. if (minTs < lruCompactTs) { lruTrackingIdx = trackingIdx; lruCompactTs = minTs; } dataPagesCnt++; } sampleSpinCnt++; if (sampleSpinCnt > SAMPLE_SPIN_LIMIT) { LT.warn(log, "Too many attempts to choose data page: " + SAMPLE_SPIN_LIMIT); return; } } if (evictDataPage(pageIdx(lruTrackingIdx))) return; evictAttemptsCnt++; } LT.warn(log, "Too many failed attempts to evict page: " + EVICT_ATTEMPTS_LIMIT); } /** {@inheritDoc} */ @Override protected boolean checkTouch(long pageId) { int trackingIdx = trackingIdx(PageIdUtils.pageIndex(pageId)); int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8); return firstTs != 0; } /** {@inheritDoc} */ @Override public void forgetPage(long pageId) { int pageIdx = PageIdUtils.pageIndex(pageId); int trackingIdx = trackingIdx(pageIdx); GridUnsafe.putLongVolatile(null, trackingArrPtr + trackingIdx * 8, 0L); } }