/* * 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.tree.io; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.HashMap; import java.util.Map; import java.util.NavigableSet; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ThreadLocalRandom; import junit.framework.TestCase; import org.apache.ignite.internal.util.GridUnsafe; /** * */ public class TrackingPageIOTest extends TestCase { /** Page size. */ public static final int PAGE_SIZE = 2048; /** */ private final TrackingPageIO io = TrackingPageIO.VERSIONS.latest(); /** * */ public void testBasics() { ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE); buf.order(ByteOrder.nativeOrder()); io.markChanged(buf, 2, 0, -1, PAGE_SIZE); assertTrue(io.wasChanged(buf, 2, 0, -1, PAGE_SIZE)); assertFalse(io.wasChanged(buf, 1, 0, -1, PAGE_SIZE)); assertFalse(io.wasChanged(buf, 3, 0, -1, PAGE_SIZE)); assertFalse(io.wasChanged(buf, 2, 1, 0, PAGE_SIZE)); } /** * */ public void testMarkingRandomly() { ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE); buf.order(ByteOrder.nativeOrder()); int cntOfPageToTrack = io.countOfPageToTrack(PAGE_SIZE); for (int i = 0; i < 1001; i++) checkMarkingRandomly(buf, i, false); } /** * */ public void testZeroingRandomly() { ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE); buf.order(ByteOrder.nativeOrder()); for (int i = 0; i < 1001; i++) checkMarkingRandomly(buf, i, true); } /** * @param buf Buffer. * @param backupId Backup id. */ private void checkMarkingRandomly(ByteBuffer buf, int backupId, boolean testZeroing) { ThreadLocalRandom rand = ThreadLocalRandom.current(); int track = io.countOfPageToTrack(PAGE_SIZE); long basePageId = io.trackingPageFor(Math.max(rand.nextLong(Integer.MAX_VALUE - track), 0), PAGE_SIZE); long maxId = testZeroing ? basePageId + rand.nextInt(1, track) : basePageId + track; assert basePageId >= 0; PageIO.setPageId(GridUnsafe.bufferAddress(buf), basePageId); Map<Long, Boolean> map = new HashMap<>(); int cntOfChanged = 0; try { for (long i = basePageId; i < basePageId + track; i++) { boolean changed = (i == basePageId || rand.nextDouble() < 0.5) && i < maxId; map.put(i, changed); if (changed) { io.markChanged(buf, i, backupId, backupId - 1, PAGE_SIZE); cntOfChanged++; } assertEquals(basePageId, PageIO.getPageId(buf)); assertEquals(cntOfChanged, io.countOfChangedPage(buf, backupId, PAGE_SIZE)); } assertEquals(cntOfChanged, io.countOfChangedPage(buf, backupId, PAGE_SIZE)); for (Map.Entry<Long, Boolean> e : map.entrySet()) assertEquals( e.getValue().booleanValue(), io.wasChanged(buf, e.getKey(), backupId, backupId -1, PAGE_SIZE)); } catch (Throwable e) { System.out.println("snapshotId = " + backupId + ", basePageId = " + basePageId); throw e; } } /** * @throws Exception If failed. */ public void testFindNextChangedPage() throws Exception { ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE); buf.order(ByteOrder.nativeOrder()); for (int i = 0; i < 101; i++) checkFindingRandomly(buf, i); } /** * @param buf Buffer. * @param backupId Backup id. */ private void checkFindingRandomly(ByteBuffer buf, int backupId) { ThreadLocalRandom rand = ThreadLocalRandom.current(); int track = io.countOfPageToTrack(PAGE_SIZE); long basePageId = io.trackingPageFor(Math.max(rand.nextLong(Integer.MAX_VALUE - track), 0), PAGE_SIZE); long maxId = basePageId + rand.nextInt(1, track); assert basePageId >= 0; PageIO.setPageId(GridUnsafe.bufferAddress(buf), basePageId); try { TreeSet<Long> setIdx = new TreeSet<>(); generateMarking(buf, track, basePageId, maxId, setIdx, backupId, backupId -1); for (long pageId = basePageId; pageId < basePageId + track; pageId++) { Long foundNextChangedPage = io.findNextChangedPage(buf, pageId, backupId, backupId - 1, PAGE_SIZE); if (io.trackingPageFor(pageId, PAGE_SIZE) == pageId) assertEquals((Long) pageId, foundNextChangedPage); else if (setIdx.contains(pageId)) assertEquals((Long) pageId, foundNextChangedPage); else { NavigableSet<Long> tailSet = setIdx.tailSet(pageId, false); Long next = tailSet.isEmpty() ? null : tailSet.first(); assertEquals(next, foundNextChangedPage); } } } catch (Throwable e) { System.out.println("snapshotId = " + backupId + ", basePageId = " + basePageId); throw e; } } /** * */ public void testMerging() { ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE); buf.order(ByteOrder.nativeOrder()); ThreadLocalRandom rand = ThreadLocalRandom.current(); int track = io.countOfPageToTrack(PAGE_SIZE); long basePageId = io.trackingPageFor(Math.max(rand.nextLong(Integer.MAX_VALUE - track), 0), PAGE_SIZE); assert basePageId >= 0; PageIO.setPageId(GridUnsafe.bufferAddress(buf), basePageId); TreeSet<Long> setIdx = new TreeSet<>(); for (int i = 0; i < 4; i++) generateMarking(buf, track, basePageId, basePageId + rand.nextInt(1, track), setIdx, i, -1); TreeSet<Long> setIdx2 = new TreeSet<>(); generateMarking(buf, track, basePageId, basePageId + rand.nextInt(1, track), setIdx2, 4, -1); assertEquals(setIdx2.size(), io.countOfChangedPage(buf, 4, PAGE_SIZE)); assertEquals(setIdx.size(), io.countOfChangedPage(buf, 3, PAGE_SIZE)); for (long i = basePageId; i < basePageId + track; i++) assertEquals("pageId = " + i, setIdx.contains(i), io.wasChanged(buf, i, 3, -1, PAGE_SIZE)); for (long i = basePageId; i < basePageId + track; i++) assertEquals("pageId = " + i, setIdx2.contains(i), io.wasChanged(buf, i, 4, 3, PAGE_SIZE)); for (long i = basePageId; i < basePageId + track; i++) assertFalse(io.wasChanged(buf, i, 5, 4, PAGE_SIZE)); } /** * */ public void testMerging_MarksShouldBeDropForSuccessfulBackup() { ByteBuffer buf = ByteBuffer.allocateDirect(PAGE_SIZE); buf.order(ByteOrder.nativeOrder()); ThreadLocalRandom rand = ThreadLocalRandom.current(); int track = io.countOfPageToTrack(PAGE_SIZE); long basePageId = io.trackingPageFor(Math.max(rand.nextLong(Integer.MAX_VALUE - track), 0), PAGE_SIZE); assert basePageId >= 0; PageIO.setPageId(GridUnsafe.bufferAddress(buf), basePageId); TreeSet<Long> setIdx = new TreeSet<>(); for (int i = 0; i < 4; i++) generateMarking(buf, track, basePageId, basePageId + rand.nextInt(1, track), setIdx, i, -1); setIdx.clear(); generateMarking(buf, track, basePageId, basePageId + rand.nextInt(1, track), setIdx, 4, -1); TreeSet<Long> setIdx2 = new TreeSet<>(); generateMarking(buf, track, basePageId, basePageId + rand.nextInt(1, track), setIdx2, 5, 3); assertEquals(setIdx.size(), io.countOfChangedPage(buf, 4, PAGE_SIZE)); assertEquals(setIdx2.size(), io.countOfChangedPage(buf, 5, PAGE_SIZE)); for (long i = basePageId; i < basePageId + track; i++) assertEquals("pageId = " + i, setIdx2.contains(i), io.wasChanged(buf, i, 5, 4, PAGE_SIZE)); } private void generateMarking( ByteBuffer buf, int track, long basePageId, long maxPageId, Set<Long> setIdx, int backupId, int successfulBackupId ) { ThreadLocalRandom rand = ThreadLocalRandom.current(); for (long i = basePageId; i < basePageId + track; i++) { boolean changed = (i == basePageId || rand.nextDouble() < 0.1) && i < maxPageId; if (changed) { io.markChanged(buf, i, backupId, successfulBackupId, PAGE_SIZE); setIdx.add(i); } } } }