Search in sources :

Example 1 with PagesListNodeIo

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);
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) PagesListMetaIo(org.apache.ignite.internal.pagememory.freelist.io.PagesListMetaIo) LongArrayList(it.unimi.dsi.fastutil.longs.LongArrayList) PagesListNodeIo(org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo) IgniteSystemProperties.getInteger(org.apache.ignite.lang.IgniteSystemProperties.getInteger) HashMap(java.util.HashMap) Map(java.util.Map)

Example 2 with PagesListNodeIo

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);
            }
        }
    }
}
Also used : PagesListNodeIo(org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo)

Example 3 with PagesListNodeIo

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);
    }
}
Also used : PagesListNodeIo(org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo)

Example 4 with PagesListNodeIo

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;
}
Also used : PagesListNodeIo(org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo)

Example 5 with PagesListNodeIo

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);
        }
    }
}
Also used : PagesListNodeIo(org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo)

Aggregations

PagesListNodeIo (org.apache.ignite.internal.pagememory.freelist.io.PagesListNodeIo)8 LongArrayList (it.unimi.dsi.fastutil.longs.LongArrayList)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 PagesListMetaIo (org.apache.ignite.internal.pagememory.freelist.io.PagesListMetaIo)1 AbstractDataPageIo (org.apache.ignite.internal.pagememory.io.AbstractDataPageIo)1 PageIo (org.apache.ignite.internal.pagememory.io.PageIo)1 IgniteSystemProperties.getBoolean (org.apache.ignite.lang.IgniteSystemProperties.getBoolean)1 IgniteSystemProperties.getInteger (org.apache.ignite.lang.IgniteSystemProperties.getInteger)1