use of org.apache.ignite.internal.processors.cache.database.freelist.io.PagesListNodeIO in project ignite by apache.
the class PagesList method init.
/**
* @param metaPageId Metadata page ID.
* @param initNew {@code True} if new list if created, {@code false} if should be initialized from metadata.
* @throws IgniteCheckedException If failed.
*/
protected final void init(long metaPageId, boolean initNew) throws IgniteCheckedException {
if (metaPageId != 0L) {
if (initNew) {
init(metaPageId, PagesListMetaIO.VERSIONS.latest());
} else {
Map<Integer, GridLongList> bucketsData = new HashMap<>();
long nextId = metaPageId;
while (nextId != 0) {
final long pageId = nextId;
final long page = acquirePage(pageId);
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=" + U.hexLong(nextId) + ", cur=" + U.hexLong(pageId) + ']';
} finally {
readUnlock(pageId, page, pageAddr);
}
} finally {
releasePage(pageId, page);
}
}
for (Map.Entry<Integer, GridLongList> e : bucketsData.entrySet()) {
int bucket = e.getKey();
long bucketSize = 0;
Stripe[] old = getBucket(bucket);
assert old == null;
long[] upd = e.getValue().array();
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);
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[bucket].set(bucketSize);
}
}
}
}
use of org.apache.ignite.internal.processors.cache.database.freelist.io.PagesListNodeIO in project ignite by apache.
the class PagesList method doMerge.
/**
* @param pageId Page ID.
* @param page Page absolute pointer.
* @param pageAddr Page address.
* @param io IO.
* @param nextId Next page ID.
* @param nextPage Next page absolute pointer.
* @param nextAddr Next page address.
* @param bucket Bucket index.
* @return Page to recycle.
* @throws IgniteCheckedException If failed.
*/
private long doMerge(long pageId, long page, long pageAddr, PagesListNodeIO io, long nextId, long nextPage, long nextAddr, int bucket) throws IgniteCheckedException {
long prevId = io.getPreviousId(pageAddr);
if (nextId == 0L)
return mergeNoNext(pageId, page, pageAddr, prevId, bucket);
else {
// No one must be able to merge it while we keep a reference.
assert getPageId(nextAddr) == nextId;
if (prevId == 0L) {
// These references must be updated at the same time in write locks.
assert PagesListNodeIO.VERSIONS.forPage(nextAddr).getPreviousId(nextAddr) == pageId;
PagesListNodeIO nextIO = PagesListNodeIO.VERSIONS.forPage(nextAddr);
nextIO.setPreviousId(nextAddr, 0);
if (needWalDeltaRecord(nextId, nextPage, null))
wal.log(new PagesListSetPreviousRecord(cacheId, nextId, 0L));
} else
// Do a fair merge: link previous and next to each other.
fairMerge(prevId, pageId, nextId, nextPage, nextAddr);
return recyclePage(pageId, page, pageAddr, null);
}
}
use of org.apache.ignite.internal.processors.cache.database.freelist.io.PagesListNodeIO in project ignite by apache.
the class PagesList method takeEmptyPage.
/**
* @param bucket Bucket index.
* @param initIoVers Optional IO to initialize page.
* @return Removed page ID.
* @throws IgniteCheckedException If failed.
*/
protected final long takeEmptyPage(int bucket, @Nullable IOVersions initIoVers) throws IgniteCheckedException {
for (int lockAttempt = 0; ; ) {
Stripe stripe = getPageForTake(bucket);
if (stripe == null)
return 0L;
final long tailId = stripe.tailId;
final long tailPage = acquirePage(tailId);
try {
// Explicit check.
long tailAddr = writeLockPage(tailId, tailPage, bucket, lockAttempt++);
if (tailAddr == 0L) {
if (isReuseBucket(bucket) && lockAttempt == TRY_LOCK_ATTEMPTS)
addStripeForReuseBucket(bucket);
continue;
}
if (stripe.empty) {
// Another thread took the last page.
writeUnlock(tailId, tailPage, tailAddr, false);
if (bucketsSize[bucket].get() > 0) {
// Ignore current attempt.
lockAttempt--;
continue;
} else
return 0L;
}
assert PageIO.getPageId(tailAddr) == tailId : "tailId = " + tailId + ", tailPageId = " + PageIO.getPageId(tailAddr);
assert PageIO.getType(tailAddr) == PageIO.T_PAGE_LIST_NODE;
boolean dirty = false;
long dataPageId;
long recycleId = 0L;
try {
PagesListNodeIO io = PagesListNodeIO.VERSIONS.forPage(tailAddr);
if (io.getNextId(tailAddr) != 0) {
// It is not a tail anymore, retry.
continue;
}
long pageId = io.takeAnyPage(tailAddr);
if (pageId != 0L) {
bucketsSize[bucket].decrementAndGet();
if (needWalDeltaRecord(tailId, tailPage, null))
wal.log(new PagesListRemovePageRecord(cacheId, tailId, pageId));
dirty = true;
dataPageId = pageId;
if (io.isEmpty(tailAddr)) {
long prevId = io.getPreviousId(tailAddr);
// to prevent empty page leak to data pages.
if (!isReuseBucket(bucket)) {
if (prevId != 0L) {
Boolean ok = write(prevId, cutTail, null, bucket, FALSE);
assert ok == TRUE : ok;
recycleId = recyclePage(tailId, tailPage, tailAddr, null);
} else
stripe.empty = true;
} else
stripe.empty = prevId == 0L;
}
} else {
// a previous page, so, the current page can be collected
assert isReuseBucket(bucket);
long prevId = io.getPreviousId(tailAddr);
assert prevId != 0L;
Boolean ok = write(prevId, cutTail, bucket, FALSE);
assert ok == TRUE : ok;
bucketsSize[bucket].decrementAndGet();
if (initIoVers != null) {
dataPageId = PageIdUtils.changeType(tailId, FLAG_DATA);
PageIO initIo = initIoVers.latest();
initIo.initNewPage(tailAddr, dataPageId, pageSize());
if (needWalDeltaRecord(tailId, tailPage, null)) {
wal.log(new InitNewPageRecord(cacheId, tailId, initIo.getType(), initIo.getVersion(), dataPageId));
}
} else
dataPageId = recyclePage(tailId, tailPage, tailAddr, null);
dirty = true;
}
// If we do not have a previous page (we are at head), then we still can return
// current page but we have to drop the whole stripe. Since it is a reuse bucket,
// we will not do that, but just return 0L, because this may produce contention on
// meta page.
} finally {
writeUnlock(tailId, tailPage, tailAddr, dirty);
}
// Put recycled page (if any) to the reuse bucket after tail is unlocked.
if (recycleId != 0L) {
assert !isReuseBucket(bucket);
reuseList.addForRecycle(new SingletonReuseBag(recycleId));
}
return dataPageId;
} finally {
releasePage(tailId, tailPage);
}
}
}
use of org.apache.ignite.internal.processors.cache.database.freelist.io.PagesListNodeIO in project ignite by apache.
the class PagesListRemovePageRecord method applyDelta.
/** {@inheritDoc} */
@Override
public void applyDelta(PageMemory pageMem, long pageAddr) throws IgniteCheckedException {
PagesListNodeIO io = PagesListNodeIO.VERSIONS.forPage(pageAddr);
boolean rmvd = io.removePage(pageAddr, rmvdPageId);
assert rmvd;
}
use of org.apache.ignite.internal.processors.cache.database.freelist.io.PagesListNodeIO in project ignite by apache.
the class PagesListSetPreviousRecord method applyDelta.
/** {@inheritDoc} */
@Override
public void applyDelta(PageMemory pageMem, long pageAddr) throws IgniteCheckedException {
PagesListNodeIO io = PagesListNodeIO.VERSIONS.forPage(pageAddr);
io.setPreviousId(pageAddr, prevPageId);
}
Aggregations