blob: 3fa24ad6622a71fb0e419945790ef7056adc89c5 [file] [log] [blame]
/*
* 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.pagemem.impl;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.internal.mem.DirectMemoryProvider;
import org.apache.ignite.internal.mem.IgniteOutOfMemoryException;
import org.apache.ignite.internal.mem.file.MappedFileMemoryProvider;
import org.apache.ignite.internal.pagemem.FullPageId;
import org.apache.ignite.internal.pagemem.PageIdAllocator;
import org.apache.ignite.internal.pagemem.PageIdUtils;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.pagemem.PageUtils;
import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl;
import org.apache.ignite.internal.processors.cache.persistence.DummyPageIO;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIO;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.testframework.junits.GridTestKernalContext;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
/**
*
*/
public class PageMemoryNoLoadSelfTest extends GridCommonAbstractTest {
/** */
protected static final int PAGE_SIZE = 8 * 1024;
/** */
private static final int MAX_MEMORY_SIZE = 10 * 1024 * 1024;
/** */
private static final PageIO PAGE_IO = new DummyPageIO();
/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
U.delete(U.resolveWorkDirectory(U.defaultWorkDirectory(), "pagemem", false));
}
/**
* @throws Exception If failed.
*/
@Test
public void testPageTearingInner() throws Exception {
PageMemory mem = memory();
mem.start();
try {
FullPageId fullId1 = allocatePage(mem);
FullPageId fullId2 = allocatePage(mem);
long page1 = mem.acquirePage(fullId1.groupId(), fullId1.pageId());
try {
long page2 = mem.acquirePage(fullId2.groupId(), fullId2.pageId());
info("Allocated pages [page1Id=" + fullId1.pageId() + ", page1=" + page1 +
", page2Id=" + fullId2.pageId() + ", page2=" + page2 + ']');
try {
writePage(mem, fullId1, page1, 1);
writePage(mem, fullId2, page2, 2);
readPage(mem, fullId1.pageId(), page1, 1);
readPage(mem, fullId2.pageId(), page2, 2);
// Check read after read.
readPage(mem, fullId1.pageId(), page1, 1);
readPage(mem, fullId2.pageId(), page2, 2);
}
finally {
mem.releasePage(fullId2.groupId(), fullId2.pageId(), page2);
}
}
finally {
mem.releasePage(fullId1.groupId(), fullId1.pageId(), page1);
}
}
finally {
mem.stop(true);
}
}
/**
* @throws Exception If failed.
*/
@Test
public void testLoadedPagesCount() throws Exception {
PageMemory mem = memory();
mem.start();
int expPages = MAX_MEMORY_SIZE / mem.systemPageSize();
try {
for (int i = 0; i < expPages * 2; i++)
allocatePage(mem);
}
catch (IgniteOutOfMemoryException e) {
e.printStackTrace();
// Expected.
assertEquals(mem.loadedPages(), expPages);
}
finally {
mem.stop(true);
}
}
/**
* @throws Exception If failed.
*/
@Test
public void testPageTearingSequential() throws Exception {
PageMemory mem = memory();
mem.start();
try {
int pagesCnt = 1024;
List<FullPageId> pages = new ArrayList<>(pagesCnt);
for (int i = 0; i < pagesCnt; i++) {
FullPageId fullId = allocatePage(mem);
pages.add(fullId);
long page = mem.acquirePage(fullId.groupId(), fullId.pageId());
try {
if (i % 64 == 0)
info("Writing page [idx=" + i + ", pageId=" + fullId.pageId() + ", page=" + page + ']');
writePage(mem, fullId, page, i + 1);
}
finally {
mem.releasePage(fullId.groupId(), fullId.pageId(), page);
}
}
for (int i = 0; i < pagesCnt; i++) {
FullPageId fullId = pages.get(i);
long page = mem.acquirePage(fullId.groupId(), fullId.pageId());
try {
if (i % 64 == 0)
info("Reading page [idx=" + i + ", pageId=" + fullId.pageId() + ", page=" + page + ']');
readPage(mem, fullId.pageId(), page, i + 1);
}
finally {
mem.releasePage(fullId.groupId(), fullId.pageId(), page);
}
}
}
finally {
mem.stop(true);
}
}
/**
* @throws Exception If failed.
*/
@Test
public void testPageHandleDeallocation() throws Exception {
PageMemory mem = memory();
mem.start();
try {
int pages = 3 * 1024 * 1024 / (8 * 1024);
Collection<FullPageId> handles = new HashSet<>();
for (int i = 0; i < pages; i++)
handles.add(allocatePage(mem));
for (FullPageId fullId : handles)
mem.freePage(fullId.groupId(), fullId.pageId());
for (int i = 0; i < pages; i++)
assertFalse(handles.add(allocatePage(mem)));
}
finally {
mem.stop(true);
}
}
/**
* @throws Exception If failed.
*/
@Test
public void testPageIdRotation() throws Exception {
PageMemory mem = memory();
mem.start();
try {
int pages = 5;
Collection<FullPageId> old = new ArrayList<>();
Collection<FullPageId> updated = new ArrayList<>();
for (int i = 0; i < pages; i++)
old.add(allocatePage(mem));
// Check that initial pages are accessible.
for (FullPageId id : old) {
long pageApsPtr = mem.acquirePage(id.groupId(), id.pageId());
try {
long pageAddr = mem.writeLock(id.groupId(), id.pageId(), pageApsPtr);
assertNotNull(pageAddr);
try {
PAGE_IO.initNewPage(pageAddr, id.pageId(), mem.realPageSize(id.groupId()), null);
long updId = PageIdUtils.rotatePageId(id.pageId());
PageIO.setPageId(pageAddr, updId);
updated.add(new FullPageId(updId, id.groupId()));
}
finally {
mem.writeUnlock(id.groupId(), id.pageId(), pageApsPtr, null, true);
}
}
finally {
mem.releasePage(id.groupId(), id.pageId(), pageApsPtr);
}
}
// Check that updated pages are inaccessible using old IDs.
for (FullPageId id : old) {
long pageApsPtr = mem.acquirePage(id.groupId(), id.pageId());
try {
long pageAddr = mem.writeLock(id.groupId(), id.pageId(), pageApsPtr);
if (pageAddr != 0L) {
mem.writeUnlock(id.groupId(), id.pageId(), pageApsPtr, null, false);
fail("Was able to acquire page write lock.");
}
mem.readLock(id.groupId(), id.pageId(), pageApsPtr);
if (pageAddr != 0) {
mem.readUnlock(id.groupId(), id.pageId(), pageApsPtr);
fail("Was able to acquire page read lock.");
}
}
finally {
mem.releasePage(id.groupId(), id.pageId(), pageApsPtr);
}
}
// Check that updated pages are accessible using new IDs.
for (FullPageId id : updated) {
long pageApsPtr = mem.acquirePage(id.groupId(), id.pageId());
try {
long pageAddr = mem.writeLock(id.groupId(), id.pageId(), pageApsPtr);
assertNotSame(0L, pageAddr);
try {
assertEquals(id.pageId(), PageIO.getPageId(pageAddr));
}
finally {
mem.writeUnlock(id.groupId(), id.pageId(), pageApsPtr, null, false);
}
pageAddr = mem.readLock(id.groupId(), id.pageId(), pageApsPtr);
assertNotSame(0L, pageAddr);
try {
assertEquals(id.pageId(), PageIO.getPageId(pageAddr));
}
finally {
mem.readUnlock(id.groupId(), id.pageId(), pageApsPtr);
}
}
finally {
mem.releasePage(id.groupId(), id.pageId(), pageApsPtr);
}
}
}
finally {
mem.stop(true);
}
}
/**
* @return Page memory implementation.
* @throws Exception If failed.
*/
protected PageMemory memory() throws Exception {
File memDir = U.resolveWorkDirectory(U.defaultWorkDirectory(), "pagemem", false);
DataRegionConfiguration plcCfg = new DataRegionConfiguration()
.setMaxSize(MAX_MEMORY_SIZE).setInitialSize(MAX_MEMORY_SIZE);
DirectMemoryProvider provider = new MappedFileMemoryProvider(log(), memDir);
return new PageMemoryNoStoreImpl(
log(),
provider,
null,
PAGE_SIZE,
plcCfg,
new DataRegionMetricsImpl(plcCfg, new GridTestKernalContext(log())),
true
);
}
/**
* @param mem Page memory.
* @param fullId Page ID.
* @param page Page pointer.
* @param val Value to write.
*/
private void writePage(PageMemory mem, FullPageId fullId, long page, int val) {
long pageAddr = mem.writeLock(-1, fullId.pageId(), page);
try {
PAGE_IO.initNewPage(pageAddr, fullId.pageId(), mem.realPageSize(fullId.groupId()), null);
for (int i = PageIO.COMMON_HEADER_END; i < PAGE_SIZE; i++)
PageUtils.putByte(pageAddr, i, (byte)val);
}
finally {
mem.writeUnlock(-1, fullId.pageId(), page, null, true);
}
}
/**
* @param mem Page memory.
* @param pageId Page ID.
* @param page Page pointer.
* @param expVal Expected value.
*/
private void readPage(PageMemory mem, long pageId, long page, int expVal) {
expVal &= 0xFF;
long pageAddr = mem.readLock(-1, pageId, page);
assert pageAddr != 0;
try {
for (int i = PageIO.COMMON_HEADER_END; i < PAGE_SIZE; i++) {
int val = PageUtils.getByte(pageAddr, i) & 0xFF;
assertEquals("Unexpected value at position: " + i, expVal, val);
}
}
finally {
mem.readUnlock(-1, pageId, page);
}
}
/**
* @param mem Memory.
* @return Page.
* @throws IgniteCheckedException If failed.
*/
public static FullPageId allocatePage(PageIdAllocator mem) throws IgniteCheckedException {
return new FullPageId(mem.allocatePage(-1, 1, PageIdAllocator.FLAG_DATA), -1);
}
}