use of org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo in project ignite-3 by apache.
the class PagesList method init.
/**
* Initializes a {@link PagesList}.
*
* @param metaPageId Metadata page ID.
* @param initNew {@code True} if new list if created, {@code false} if should be initialized from metadata.
* @throws IgniteInternalCheckedException If failed.
*/
protected final void init(long metaPageId, boolean initNew) throws IgniteInternalCheckedException {
if (metaPageId != 0L) {
if (initNew) {
init(metaPageId, PagesListMetaIo.VERSIONS.latest());
} else {
Map<Integer, LongArrayList> bucketsData = new HashMap<>();
long nextId = metaPageId;
while (nextId != 0) {
final long pageId = nextId;
final long page = acquirePage(pageId, IoStatisticsHolderNoOp.INSTANCE);
try {
// No concurrent recycling on init.
long pageAddr = readLock(pageId, page);
assert pageAddr != 0L;
try {
PagesListMetaIo io = PagesListMetaIo.VERSIONS.forPage(pageAddr);
io.getBucketsData(pageAddr, bucketsData);
nextId = io.getNextMetaPageId(pageAddr);
assert nextId != pageId : "Loop detected [next=" + hexLong(nextId) + ", cur=" + hexLong(pageId) + ']';
} finally {
readUnlock(pageId, page, pageAddr);
}
} finally {
releasePage(pageId, page);
}
}
for (Map.Entry<Integer, LongArrayList> e : bucketsData.entrySet()) {
int bucket = e.getKey();
long bucketSize = 0;
Stripe[] old = getBucket(bucket);
assert old == null;
long[] upd = e.getValue().toLongArray();
Stripe[] tails = new Stripe[upd.length];
for (int i = 0; i < upd.length; i++) {
long tailId = upd[i];
long prevId = tailId;
int cnt = 0;
while (prevId != 0L) {
final long pageId = prevId;
final long page = acquirePage(pageId, IoStatisticsHolderNoOp.INSTANCE);
try {
long pageAddr = readLock(pageId, page);
assert pageAddr != 0L;
try {
PagesListNodeIo io = PagesListNodeIo.VERSIONS.forPage(pageAddr);
cnt += io.getCount(pageAddr);
prevId = io.getPreviousId(pageAddr);
// In reuse bucket the page itself can be used as a free page.
if (isReuseBucket(bucket) && prevId != 0L) {
cnt++;
}
} finally {
readUnlock(pageId, page, pageAddr);
}
} finally {
releasePage(pageId, page);
}
}
Stripe stripe = new Stripe(tailId, cnt == 0);
tails[i] = stripe;
bucketSize += cnt;
}
boolean ok = casBucket(bucket, null, tails);
assert ok;
bucketsSize.set(bucket, bucketSize);
}
}
}
}
use of org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo in project ignite-3 by apache.
the class PagesList method merge.
private long merge(final long pageId, final long page, long nextId, int bucket, IoStatisticsHolder statHolder) throws IgniteInternalCheckedException {
// We should do mergeNoNext then.
assert nextId != 0;
// Lock all the pages in correct order (from next to previous) and do the merge in retry loop.
for (; ; ) {
final long curId = nextId;
final long curPage = curId == 0L ? 0L : acquirePage(curId, statHolder);
try {
boolean write = false;
// Explicit check.
final long curAddr = curPage == 0L ? 0L : writeLock(curId, curPage);
// Explicit check.
final long pageAddr = writeLock(pageId, page);
if (pageAddr == 0L) {
if (curAddr != 0L) {
// Unlock next page if needed.
writeUnlock(curId, curPage, curAddr, false);
}
// Someone has merged or taken our empty page concurrently. Nothing to do here.
return 0L;
}
try {
PagesListNodeIo io = PagesListNodeIo.VERSIONS.forPage(pageAddr);
if (!io.isEmpty(pageAddr)) {
// No need to merge anymore.
return 0L;
}
// Check if we see a consistent state of the world.
if (io.getNextId(pageAddr) == curId && (curId == 0L) == (curAddr == 0L)) {
long recycleId = doMerge(pageId, pageAddr, io, curId, curAddr, bucket, statHolder);
write = true;
// Done.
return recycleId;
}
// Reread next page ID and go for retry.
nextId = io.getNextId(pageAddr);
} finally {
if (curAddr != 0L) {
writeUnlock(curId, curPage, curAddr, write);
}
writeUnlock(pageId, page, pageAddr, write);
}
} finally {
if (curPage != 0L) {
releasePage(curId, curPage);
}
}
}
}
use of org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo in project ignite-3 by apache.
the class PagesList method fairMerge.
/**
* Link previous and next to each other.
*
* @param prevId Previous Previous page ID.
* @param pageId Page ID.
* @param nextId Next page ID.
* @param nextAddr Next page address.
* @param statHolder Statistics holder to track IO operations.
* @throws IgniteInternalCheckedException If failed.
*/
private void fairMerge(final long prevId, long pageId, long nextId, long nextAddr, IoStatisticsHolder statHolder) throws IgniteInternalCheckedException {
long prevPage = acquirePage(prevId, statHolder);
try {
// No check, we keep a reference.
final long prevAddr = writeLock(prevId, prevPage);
assert prevAddr != 0L;
try {
PagesListNodeIo prevIo = PagesListNodeIo.VERSIONS.forPage(prevAddr);
PagesListNodeIo nextIo = PagesListNodeIo.VERSIONS.forPage(nextAddr);
// These references must be updated at the same time in write locks.
assert prevIo.getNextId(prevAddr) == pageId;
assert nextIo.getPreviousId(nextAddr) == pageId;
prevIo.setNextId(prevAddr, nextId);
nextIo.setPreviousId(nextAddr, prevId);
} finally {
writeUnlock(prevId, prevPage, prevAddr, true);
}
} finally {
releasePage(prevId, prevPage);
}
}
use of org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo in project ignite-3 by apache.
the class PagesList method storedPagesCount.
/**
* !!! For tests only, does not provide any correctness guarantees for concurrent access.
*
* @param bucket Bucket index.
* @return Number of pages stored in this list.
* @throws IgniteInternalCheckedException If failed.
*/
protected final long storedPagesCount(int bucket) throws IgniteInternalCheckedException {
long res = 0;
Stripe[] tails = getBucket(bucket);
if (tails != null) {
for (Stripe tail : tails) {
long tailId = tail.tailId;
while (tailId != 0L) {
final long pageId = tailId;
final long page = acquirePage(pageId, IoStatisticsHolderNoOp.INSTANCE);
try {
long pageAddr = readLock(pageId, page);
assert pageAddr != 0L;
try {
PagesListNodeIo io = PagesListNodeIo.VERSIONS.forPage(pageAddr);
int cnt = io.getCount(pageAddr);
assert cnt >= 0;
res += cnt;
tailId = io.getPreviousId(pageAddr);
// In reuse bucket the page itself can be used as a free page.
if (isReuseBucket(bucket) && tailId != 0L) {
res++;
}
} finally {
readUnlock(pageId, page, pageAddr);
}
} finally {
releasePage(pageId, page);
}
}
}
}
assert res == bucketsSize.get(bucket) : "Wrong bucket size counter [exp=" + res + ", cntr=" + bucketsSize.get(bucket) + ']';
return res;
}
use of org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo in project ignite-3 by apache.
the class PagesList method put.
/**
* Puts data page.
*
* @param bag Reuse bag.
* @param dataId Data page ID.
* @param dataAddr Data page address.
* @param bucket Bucket.
* @param statHolder Statistics holder to track IO operations.
* @throws IgniteInternalCheckedException If failed.
*/
protected final void put(@Nullable ReuseBag bag, final long dataId, final long dataAddr, int bucket, IoStatisticsHolder statHolder) throws IgniteInternalCheckedException {
assert bag == null ^ dataAddr == 0L;
if (bag != null && bag.isEmpty()) {
// Skip allocating stripe for empty bag.
return;
}
if (bag == null && onheapListCachingEnabled && putDataPage(getBucketCache(bucket, true), dataId, dataAddr, bucket)) {
// Successfully put page to the onheap pages list cache.
if (log.isDebugEnabled()) {
log.debug("Put page to pages list cache [list=" + name() + ", bucket=" + bucket + ", dataId=" + dataId + ']');
}
return;
}
for (int lockAttempt = 0; ; ) {
Stripe stripe = getStripeForPut(bucket, bag);
// No need to continue if bag has been utilized at getPageForPut (free page can be used for pagelist).
if (bag != null && bag.isEmpty()) {
return;
}
final long tailId = stripe.tailId;
// Stripe was removed from bucket concurrently.
if (tailId == 0L) {
continue;
}
final long tailPage = acquirePage(tailId, statHolder);
try {
// Explicit check.
long tailAddr = writeLockPage(tailId, tailPage, bucket, lockAttempt++, bag);
if (tailAddr == 0L) {
// No need to continue if bag has been utilized at writeLockPage.
if (bag != null && bag.isEmpty()) {
return;
} else {
continue;
}
}
if (stripe.tailId != tailId) {
// Another thread took the last page.
writeUnlock(tailId, tailPage, tailAddr, false);
// Ignore current attempt.
lockAttempt--;
continue;
}
assert getPageId(tailAddr) == tailId : "tailId = " + hexLong(tailId) + ", pageId = " + hexLong(getPageId(tailAddr));
assert getType(tailAddr) == T_PAGE_LIST_NODE : "tailId = " + hexLong(tailId) + ", type = " + getType(tailAddr);
boolean ok = false;
try {
PagesListNodeIo io = pageMem.ioRegistry().resolve(tailAddr);
ok = bag != null ? // Here we can always take pages from the bag to build our list.
putReuseBag(tailId, tailAddr, io, bag, bucket, statHolder) : // the deadlock is impossible as well.
putDataPage(tailId, tailAddr, io, dataId, dataAddr, bucket, statHolder);
if (ok) {
if (log.isDebugEnabled()) {
log.debug("Put page to pages list [list=" + name() + ", bucket=" + bucket + ", dataId=" + dataId + ", tailId=" + tailId + ']');
}
stripe.empty = false;
return;
}
} finally {
writeUnlock(tailId, tailPage, tailAddr, ok);
}
} finally {
releasePage(tailId, tailPage);
}
}
}
Aggregations