From f419ead0d7d035a5a881e07d5ddb5e3e4277dfd4 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 18 Mar 2019 20:21:35 +0300 Subject: [PATCH 01/18] IGNITE-7935 (wip) Batch write to pagemem, fake invokeAll implementation. --- .../jmh/pagemem/JmhBatchUpdatesBenchmark.java | 423 ++++++++++++++++ .../apache/ignite/IgniteSystemProperties.java | 3 + .../processors/cache/BatchedCacheEntries.java | 479 ++++++++++++++++++ .../processors/cache/CacheMetricsImpl.java | 9 + .../processors/cache/GridCacheMapEntry.java | 96 ++++ .../cache/IgniteCacheOffheapManager.java | 31 ++ .../cache/IgniteCacheOffheapManagerImpl.java | 77 +++ .../preloader/GridDhtPartitionDemander.java | 171 ++++++- .../cache/persistence/DataStructure.java | 21 + .../persistence/GridCacheOffheapManager.java | 13 +- .../IgniteCacheDatabaseSharedManager.java | 2 +- .../cache/persistence/RowStore.java | 20 + .../freelist/AbstractFreeList.java | 279 +++++++++- .../cache/persistence/freelist/FreeList.java | 7 + .../cache/persistence/tree/BPlusTree.java | 5 + .../tree/io/AbstractDataPageIO.java | 73 ++- .../persistence/tree/util/PageHandler.java | 95 ++++ .../processors/cache/tree/CacheDataTree.java | 86 ++++ .../ignite/internal/util/IgniteTree.java | 31 ++ .../FreeListPreloadWithBatchUpdatesTest.java | 342 +++++++++++++ 20 files changed, 2241 insertions(+), 22 deletions(-) create mode 100644 modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java new file mode 100644 index 0000000000000..a6e565f337132 --- /dev/null +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java @@ -0,0 +1,423 @@ +/* + * 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.benchmarks.jmh.pagemem; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.IgniteException; +import org.apache.ignite.Ignition; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataRegionConfiguration; +import org.apache.ignite.configuration.DataStorageConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.processors.cache.CacheObject; +import org.apache.ignite.internal.processors.cache.GridCacheContext; +import org.apache.ignite.internal.processors.cache.GridCacheEntryInfo; +import org.apache.ignite.internal.processors.cache.KeyCacheObject; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionDemander; +import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPreloader; +import org.apache.ignite.logger.NullLogger; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Batch updates in pagemem through preloader. + * + * todo benchmark for internal testing purposes. + */ +@BenchmarkMode(Mode.AverageTime) +@Fork(value = 1, jvmArgsAppend = {"-Xms3g", "-Xmx3g", "-server", "-XX:+AggressiveOpts", "-XX:MaxMetaspaceSize=256m"}) +@Measurement(iterations = 11) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@State(Scope.Benchmark) +@Threads(1) +@Warmup(iterations = 15) +public class JmhBatchUpdatesBenchmark { + /** */ + private static final long DEF_REG_SIZE = 3 * 1024 * 1024 * 1024L; + + /** */ + private static final int BATCH_SIZE = 500; + + /** */ + private static final String REG_BATCH = "batch-region"; + + /** */ + private static final String REG_SINGLE = "single-region"; + + /** */ + private static final String CACHE_BATCH = "batch"; + + /** */ + private static final String CACHE_SINGLE = "single"; + + /** */ + private static final String NODE_NAME = "srv0"; + + /** */ + private static int iteration = 0; + + /** */ + public enum RANGE { + /** */ + r0_4(0, 4), + + /** */ + r4_16(4, 16), + + /** */ + r16_64(16, 64), + + /** */ + r100_200(100, 200), + + /** */ + r200_500(200, 500), + + /** */ + r500_800(500, 800), + + /** */ + r800_1200(800, 1200), + + /** */ + r2000_3000(2_000, 3_000), + + /** */ + r1000_8000(1_000, 8_000), + + /** Large objects only. */ + r4000_16000(4_000, 16_000), + + /** Mixed objects, mostly large objects. */ + r0_32000(100, 32_000); + + /** */ + private final int min; + + /** */ + private final int max; + + /** */ + RANGE(int min, int max) { + this.min = min; + this.max = max; + } + } + + + /** + * Create Ignite configuration. + * + * @return Ignite configuration. + */ + private IgniteConfiguration getConfiguration(String cfgName) { + IgniteConfiguration cfg = new IgniteConfiguration(); + + cfg.setGridLogger(new NullLogger()); + + cfg.setIgniteInstanceName(cfgName); + + DataRegionConfiguration reg1 = new DataRegionConfiguration(); + reg1.setInitialSize(DEF_REG_SIZE); + reg1.setMaxSize(DEF_REG_SIZE); + reg1.setName(REG_BATCH); + + DataRegionConfiguration reg2 = new DataRegionConfiguration(); + reg2.setInitialSize(DEF_REG_SIZE); + reg2.setMaxSize(DEF_REG_SIZE); + reg2.setName(REG_SINGLE); + + DataStorageConfiguration storeCfg = new DataStorageConfiguration(); + + storeCfg.setDataRegionConfigurations(reg1, reg2); + + cfg.setDataStorageConfiguration(storeCfg); + + cfg.setCacheConfiguration(ccfg(false), ccfg(true)); + + return cfg; + } + + /** + * @return Cache configuration. + */ + private CacheConfiguration ccfg(boolean batch) { + return new CacheConfiguration(batch ? CACHE_BATCH : CACHE_SINGLE) + .setAffinity(new RendezvousAffinityFunction(false, 1)) + .setCacheMode(CacheMode.REPLICATED) + .setAtomicityMode(CacheAtomicityMode.ATOMIC) + .setDataRegionName(batch ? REG_BATCH : REG_SINGLE); + } + + /** + * Test single updates. + * + * @param data Data that will be preloaded. + * @param preloader Data preloader. + */ + @Benchmark + public void checkSingle(Data data, Preloader preloader) throws IgniteCheckedException { + preloader.demanderSingle.preloadEntriesSingle(null, 0, data.singleData, data.cctxSingle.topology().readyTopologyVersion()); + } + + /** + * Test batch updates. + * + * @param data Data that will be preloaded. + * @param preloader Data preloader. + */ + @Benchmark + public void checkBatch(Data data, Preloader preloader) throws IgniteCheckedException { + preloader.demanderBatch.preloadEntriesBatch(null, 0, data.batchData, data.cctxBatch.topology().readyTopologyVersion()); + } + + + /** + * Start 2 servers and 1 client. + */ + @Setup(Level.Trial) + public void setup() { + Ignition.start(getConfiguration(NODE_NAME)); + } + + /** + * Stop all grids after tests. + */ + @TearDown(Level.Trial) + public void tearDown() { + Ignition.stopAll(true); + } + + /** + * Create streamer on client cache. + */ + @State(Scope.Benchmark) + public static class Preloader { + /** */ + final GridDhtPartitionDemander demanderBatch = demander(CACHE_BATCH); + + /** */ + final GridDhtPartitionDemander demanderSingle = demander(CACHE_SINGLE); + + /** */ + GridDhtPartitionDemander demander(String name) { + GridCacheContext cctx = ((IgniteEx)Ignition.ignite(NODE_NAME)).cachex(name).context(); + + GridDhtPreloader preloader = (GridDhtPreloader)cctx.group().preloader(); + + return getFieldValue(preloader, "demander"); + } + + /** + * Get object field value via reflection. + * + * @param obj Object or class to get field value from. + * @param fieldNames Field names to get value for: obj->field1->field2->...->fieldN. + * @param Expected field class. + * @return Field value. + * @throws IgniteException In case of error. + */ + public static T getFieldValue(Object obj, String... fieldNames) throws IgniteException { + assert obj != null; + assert fieldNames != null; + assert fieldNames.length >= 1; + + try { + for (String fieldName : fieldNames) { + Class cls = obj instanceof Class ? (Class)obj : obj.getClass(); + + try { + obj = findField(cls, obj, fieldName); + } + catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + return (T)obj; + } + catch (IllegalAccessException e) { + throw new IgniteException("Failed to get object field [obj=" + obj + + ", fieldNames=" + Arrays.toString(fieldNames) + ']', e); + } + } + + /** + * @param cls Class for searching. + * @param obj Target object. + * @param fieldName Field name for search. + * @return Field from object if it was found. + */ + private static Object findField(Class cls, Object obj, + String fieldName) throws NoSuchFieldException, IllegalAccessException { + // Resolve inner field. + Field field = cls.getDeclaredField(fieldName); + + boolean accessible = field.isAccessible(); + + if (!accessible) + field.setAccessible(true); + + return field.get(obj); + } + } + + /** + * Prepare and clean collection with streaming data. + */ + @State(Scope.Thread) + public static class Data { + /** */ + @Param + private RANGE range; + + /** */ + private int[] sizes; + + /** */ + Collection batchData = new ArrayList<>(BATCH_SIZE); + + /** */ + Collection singleData = new ArrayList<>(BATCH_SIZE); + + /** */ + GridCacheContext cctxBatch = ((IgniteEx)Ignition.ignite(NODE_NAME)).cachex(CACHE_BATCH).context(); + + /** */ + GridCacheContext cctxSingle = ((IgniteEx)Ignition.ignite(NODE_NAME)).cachex(CACHE_SINGLE).context(); + + /** */ + @Setup(Level.Trial) + public void setup() { + sizes = sizes(range.min, range.max, BATCH_SIZE); + } + + /** + * Prepare collection. + */ + @Setup(Level.Iteration) + public void prepare() { + int iter = iteration++; + + int off = iter * BATCH_SIZE; + + batchData = prepareBatch(cctxBatch, off, BATCH_SIZE, sizes); + singleData = prepareBatch(cctxSingle, off, BATCH_SIZE, sizes); + } + + /** + * Clean collection after each test. + */ + @TearDown(Level.Iteration) + public void cleanCollection() { + batchData = null; + singleData = null; + } + + /** */ + int[] sizes(int minObjSize, int maxObjSize, int batchSize) { + int sizes[] = new int[batchSize]; + int minSize = maxObjSize; + int maxSize = minObjSize; + + int delta = maxObjSize - minObjSize; + + for (int i = 0; i < batchSize; i++) { + int size = sizes[i] = minObjSize + (delta > 0 ? ThreadLocalRandom.current().nextInt(delta) : 0); + + if (size < minSize) + minSize = size; + + if (size > maxSize) + maxSize = size; + } + + return sizes; + } + + /** + * Generates rebalance info objects. + * + * @param cctx Cache context. + * @param off Offset. + * @param cnt Count. + * @param sizes Object sizes. + * @return List of generated objects. + */ + private List prepareBatch(GridCacheContext cctx, int off, int cnt, int[] sizes) { + List infos = new ArrayList<>(); + + for (int i = off; i < off + cnt; i++) { + int size = sizes[i - off]; + + KeyCacheObject key = cctx.toCacheKeyObject(i); + CacheObject val = cctx.toCacheObject(new byte[size]); + + GridCacheEntryInfo info = new GridCacheEntryInfo(); + info.key(key); + info.value(val); + info.cacheId(cctx.cacheId()); + info.version(cctx.shared().versions().startVersion()); + + infos.add(info); + } + + return infos; + } + } + + /** + * Run benchmark. + * + * @param args Args. + */ + public static void main(String[] args) throws RunnerException { + final Options options = new OptionsBuilder() + .include(JmhBatchUpdatesBenchmark.class.getSimpleName()) + .build(); + + new Runner(options).run(); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java index 3097a050c31f2..6fdf2968f7747 100644 --- a/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java +++ b/modules/core/src/main/java/org/apache/ignite/IgniteSystemProperties.java @@ -1097,6 +1097,9 @@ public final class IgniteSystemProperties { */ public static final String IGNITE_DISCOVERY_DISABLE_CACHE_METRICS_UPDATE = "IGNITE_DISCOVERY_DISABLE_CACHE_METRICS_UPDATE"; + /** */ + public static final String IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE = "IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE"; + /** * Maximum number of different partitions to be extracted from between expression within sql query. * In case of limit exceeding all partitions will be used. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java new file mode 100644 index 0000000000000..b8b2e3590922f --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java @@ -0,0 +1,479 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; +import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtInvalidPartitionException; +import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition; +import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; +import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow; +import org.apache.ignite.internal.processors.cache.tree.DataRow; +import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; +import org.apache.ignite.internal.processors.dr.GridDrType; +import org.apache.ignite.internal.util.IgniteTree; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.T3; +import org.apache.ignite.internal.util.typedef.internal.CU; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.jetbrains.annotations.Nullable; + +import static org.apache.ignite.internal.processors.cache.GridCacheMapEntry.ATOMIC_VER_COMPARATOR; +import static org.apache.ignite.internal.util.IgniteTree.OperationType.NOOP; +import static org.apache.ignite.internal.util.IgniteTree.OperationType.PUT; +import static org.apache.ignite.internal.util.IgniteTree.OperationType.REMOVE; + +/** + * Batch of cache entries to optimize page memory processing. + */ +public class BatchedCacheEntries { + /** */ + private final GridDhtLocalPartition part; + + /** */ + private final GridCacheContext cctx; + + /** */ + private final LinkedHashMap infos = new LinkedHashMap<>(); + + /** */ + private final AffinityTopologyVersion topVer; + + /** */ + private final boolean preload; + + /** */ + private List entries; + + /** */ + private int skipped; + + /** */ + public BatchedCacheEntries(AffinityTopologyVersion topVer, int partId, GridCacheContext cctx, boolean preload) { + this.topVer = topVer; + this.cctx = cctx; + this.preload = preload; + this.part = cctx.topology().localPartition(partId, topVer, true, true); + } + + /** */ + public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { + // todo remove `key` duplication (Map keys() { + return infos.keySet(); + } + + /** */ + public Collection values() { + return infos.values(); + } + + /** */ + public GridDhtLocalPartition part() { + return part; + } + + /** */ + public GridCacheContext context() { + return cctx; + } + + /** */ + public CacheMapEntryInfo get(KeyCacheObject key) { + return infos.get(key); + } + + /** */ + public boolean preload() { + return preload; + } + + /** */ + public void onRemove(KeyCacheObject key) { + // todo - remove from original collection + ++skipped; + } + + /** */ + public void onError(KeyCacheObject key, IgniteCheckedException e) { + // todo - remove from original collection + ++skipped; + } + + /** */ + public boolean skip(KeyCacheObject key) { + // todo + return false; + } + + /** */ + public List lock() { + return entries = lockEntries(infos.values(), topVer); + } + + /** */ + public void unlock() { + unlockEntries(infos.values(), topVer); + } + + /** */ + public int size() { + return infos.size() - skipped; + } + + /** */ + private List lockEntries(Collection list, AffinityTopologyVersion topVer) + throws GridDhtInvalidPartitionException { + List locked = new ArrayList<>(list.size()); + + while (true) { + for (CacheMapEntryInfo info : list) { + GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(info.key(), topVer); + + locked.add(entry); + + info.cacheEntry(entry); + } + + boolean retry = false; + + for (int i = 0; i < locked.size(); i++) { + GridCacheMapEntry entry = locked.get(i); + + if (entry == null) + continue; + + // todo ensure free space + // todo check obsolete + + entry.lockEntry(); + + if (entry.obsolete()) { + // Unlock all locked. + for (int j = 0; j <= i; j++) { + if (locked.get(j) != null) + locked.get(j).unlockEntry(); + } + + // Clear entries. + locked.clear(); + + // Retry. + retry = true; + + break; + } + } + + if (!retry) + return locked; + } + } + + /** + * Releases java-level locks on cache entries + * todo carefully think about possible reorderings in locking/unlocking. + * + * @param locked Locked entries. + * @param topVer Topology version. + */ + private void unlockEntries(Collection locked, AffinityTopologyVersion topVer) { + // Process deleted entries before locks release. + assert cctx.deferredDelete() : this; + + // Entries to skip eviction manager notification for. + // Enqueue entries while holding locks. + // todo Common skip list. + Collection skip = null; + + int size = locked.size(); + + try { + for (CacheMapEntryInfo info : locked) { + GridCacheMapEntry entry = info.cacheEntry(); + + if (entry != null && entry.deleted()) { + if (skip == null) + skip = U.newHashSet(locked.size()); + + skip.add(entry.key()); + } + + try { + info.updateCacheEntry(); + } catch (IgniteCheckedException e) { + skip.add(entry.key()); + } + } + } + finally { + // At least RuntimeException can be thrown by the code above when GridCacheContext is cleaned and there is + // an attempt to use cleaned resources. + // That's why releasing locks in the finally block.. + for (CacheMapEntryInfo info : locked) { + GridCacheMapEntry entry = info.cacheEntry(); + if (entry != null) + entry.unlockEntry(); + } + } + + // Try evict partitions. + for (CacheMapEntryInfo info : locked) { + GridDhtCacheEntry entry = info.cacheEntry(); + if (entry != null) + entry.onUnlock(); + } + + if (skip != null && skip.size() == size) + // Optimization. + return; + + // Must touch all entries since update may have deleted entries. + // Eviction manager will remove empty entries. + for (CacheMapEntryInfo info : locked) { + GridCacheMapEntry entry = info.cacheEntry(); + if (entry != null && (skip == null || !skip.contains(entry.key()))) + entry.touch(); + } + } + + /** */ + public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvokeAllClosure { + /** */ + private final List> resBatch = new ArrayList<>(entries.size()); + + /** */ + private final int cacheId = context().group().storeCacheIdInDataPage() ? context().cacheId() : CU.UNDEFINED_CACHE_ID; + + /** */ + private final int partId = part().id(); + + /** {@inheritDoc} */ + @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { + List newRows = new ArrayList<>(16); + + for (T2 t2 : rows) { + CacheDataRow oldRow = t2.get1(); + + KeyCacheObject key = t2.get2().key(); + + CacheMapEntryInfo newRowInfo = get(key); + + try { + if (newRowInfo.needUpdate(oldRow)) { + CacheDataRow newRow; + + CacheObject val = newRowInfo.value(); + + if (val != null) { + if (oldRow != null) { + // todo think about batch updates + newRow = context().offheap().dataStore(part()).createRow( + context(), + key, + newRowInfo.value(), + newRowInfo.version(), + newRowInfo.expireTime(), + oldRow); + } + else { + CacheObjectContext coCtx = context().cacheObjectContext(); + // todo why we need this + val.valueBytes(coCtx); + key.valueBytes(coCtx); + + if (key.partition() == -1) + key.partition(partId); + + newRow = new DataRow(key, val, newRowInfo.version(), partId, newRowInfo.expireTime(), cacheId); + + newRows.add(newRow); + } + + IgniteTree.OperationType treeOp = oldRow != null && oldRow.link() == newRow.link() ? + NOOP : PUT; + + resBatch.add(new T3<>(treeOp, oldRow, newRow)); + } + else { + // todo we should pass key somehow to remove old row (because in particular case oldRow should not contain key) + newRow = new DataRow(key, null, null, 0, 0, 0); + + resBatch.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); + } + } + } + catch (GridCacheEntryRemovedException e) { + onRemove(key); + } + } + + if (!newRows.isEmpty()) + context().offheap().dataStore(part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + } + + /** {@inheritDoc} */ + @Override public Collection> result() { + return resBatch; + } + + /** {@inheritDoc} */ + @Override public boolean apply(CacheDataRow row) { + return false; + } + } + + /** */ + public static class CacheMapEntryInfo { + /** todo think about remove */ + private final BatchedCacheEntries batch; + + /** */ + private final KeyCacheObject key; + + /** */ + private final CacheObject val; + + /** */ + private final long expTime; + + /** */ + private final long ttl; + + /** */ + private final GridCacheVersion ver; + + /** */ + private final GridDrType drType; + + /** */ + private GridDhtCacheEntry entry; + + /** */ + private boolean update; + + /** */ + public CacheMapEntryInfo( + BatchedCacheEntries batch, + KeyCacheObject key, + CacheObject val, + long expTime, + long ttl, + GridCacheVersion ver, + GridDrType drType + ) { + this.batch = batch; + this.key = key; + this.val = val; + this.expTime = expTime; + this.ver = ver; + this.drType = drType; + this.ttl = ttl; + } + + /** + * @return Key. + */ + public KeyCacheObject key() { + return key; + } + + /** + * @return Version. + */ + public GridCacheVersion version() { + return ver; + } + + /** + * @return Value. + */ + public CacheObject value() { + return val; + } + + /** + * @return Expire time. + */ + public long expireTime() { + return expTime; + } + + /** + * @param entry Cache entry. + */ + public void cacheEntry(GridDhtCacheEntry entry) { + this.entry = entry; + } + + /** + * @return Cache entry. + */ + public GridDhtCacheEntry cacheEntry() { + return entry; + } + + /** */ + public void updateCacheEntry() throws IgniteCheckedException { + if (!update) + return; + + entry.finishInitialUpdate(val, expTime, ttl, ver, batch.topVer, drType, null, batch.preload); + } + + /** */ + public boolean needUpdate(CacheDataRow row) throws GridCacheEntryRemovedException { + GridCacheVersion currVer = row != null ? row.version() : entry.version(); + + GridCacheContext cctx = batch.context(); + + boolean isStartVer = cctx.versions().isStartVersion(currVer); + + boolean update0; + + if (cctx.group().persistenceEnabled()) { + if (!isStartVer) { + if (cctx.atomic()) + update0 = ATOMIC_VER_COMPARATOR.compare(currVer, version()) < 0; + else + update0 = currVer.compareTo(version()) < 0; + } + else + update0 = true; + } + else + update0 = (isStartVer && row == null); + + update0 |= (!batch.preload() && entry.deletedUnlocked()); + + update = update0; + + return update0; + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java index 8ce21c59de3d1..aa88b2e1bb04e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMetricsImpl.java @@ -1163,6 +1163,15 @@ public void onRebalanceKeyReceived() { rebalancingKeysRate.onHit(); } + /** + * Rebalance entry store callback. + */ + public void onRebalanceKeysReceived(long batchSize) { + rebalancedKeys.addAndGet(batchSize); + + rebalancingKeysRate.onHits(batchSize); + } + /** * Rebalance supply message callback. * diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java index 2428290d38a20..ced399e0c9c4a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java @@ -3502,6 +3502,102 @@ else if (deletedUnlocked()) } } + /** + * todo explain this and remove code duplication + * @param val New value. + * @param expireTime Expiration time. + * @param ttl Time to live. + * @param ver Version to use. + * @param topVer Topology version. + * @param drType DR type. + * @param mvccVer Mvcc version. + * @param preload Flag indicating whether entry is being preloaded. + * @throws IgniteCheckedException In case of error. + */ + protected void finishInitialUpdate( + @Nullable CacheObject val, + long expireTime, + long ttl, + GridCacheVersion ver, + AffinityTopologyVersion topVer, + GridDrType drType, + MvccVersion mvccVer, + boolean preload + ) throws IgniteCheckedException { + boolean fromStore = false; + boolean walEnabled = !cctx.isNear() && cctx.group().persistenceEnabled() && cctx.group().walEnabled(); + + update(val, expireTime, ttl, ver, true); + + boolean skipQryNtf = false; + + if (val == null) { + skipQryNtf = true; + + if (cctx.deferredDelete() && !deletedUnlocked() && !isInternal()) + deletedUnlocked(true); + } + else if (deletedUnlocked()) + deletedUnlocked(false); + + long updateCntr = 0; + + if (!preload) + updateCntr = nextPartitionCounter(topVer, true, null); + + if (walEnabled) { + if (cctx.mvccEnabled()) { + cctx.shared().wal().log(new MvccDataRecord(new MvccDataEntry( + cctx.cacheId(), + key, + val, + val == null ? DELETE : GridCacheOperation.CREATE, + null, + ver, + expireTime, + partition(), + updateCntr, + mvccVer == null ? MvccUtils.INITIAL_VERSION : mvccVer + ))); + } else { + cctx.shared().wal().log(new DataRecord(new DataEntry( + cctx.cacheId(), + key, + val, + val == null ? DELETE : GridCacheOperation.CREATE, + null, + ver, + expireTime, + partition(), + updateCntr + ))); + } + } + + drReplicate(drType, val, ver, topVer); + + if (!skipQryNtf) { + cctx.continuousQueries().onEntryUpdated( + key, + val, + null, + this.isInternal() || !this.context().userCache(), + this.partition(), + true, + true, + updateCntr, + null, + topVer); + } + + onUpdateFinished(updateCntr); + + if (!fromStore && cctx.store().isLocal()) { + if (val != null) + cctx.store().put(null, key, val, ver); + } + } + /** * @param cntr Updated partition counter. */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index b7e8ec717fc38..c9dace3ae682d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache; +import java.util.Collection; import java.util.List; import java.util.Map; import javax.cache.Cache; @@ -47,6 +48,7 @@ import org.apache.ignite.internal.util.lang.GridIterator; import org.apache.ignite.internal.util.lang.IgniteInClosure2X; import org.apache.ignite.lang.IgniteBiTuple; +import org.apache.ignite.lang.IgnitePredicate; import org.jetbrains.annotations.Nullable; /** @@ -188,6 +190,20 @@ public boolean expire(GridCacheContext cctx, IgniteInClosure2X keys, + GridDhtLocalPartition part, + OffheapInvokeAllClosure c + ) throws IgniteCheckedException; + /** * @param cctx Cache context. * @param key Key. @@ -579,6 +595,13 @@ interface OffheapInvokeClosure extends IgniteTree.InvokeClosure { @Nullable public CacheDataRow oldRow(); } + /** + * + */ + interface OffheapInvokeAllClosure extends IgniteTree.InvokeAllClosure, IgnitePredicate { +// boolean preload(); + } + /** * */ @@ -861,6 +884,14 @@ MvccUpdateResult mvccLock( */ public void invoke(GridCacheContext cctx, KeyCacheObject key, OffheapInvokeClosure c) throws IgniteCheckedException; + /** + * @param cctx Cache context. + * @param keys Keys. + * @param c Closure. + * @throws IgniteCheckedException If failed. + */ + public void invokeAll(GridCacheContext cctx, Collection keys, OffheapInvokeAllClosure c) throws IgniteCheckedException; + /** * * @param cctx Cache context. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java index 04443be2cf7d7..3f9d2ccbbfb51 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManagerImpl.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.cache; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -103,6 +104,7 @@ import org.apache.ignite.internal.util.lang.GridIterator; import org.apache.ignite.internal.util.lang.IgniteInClosure2X; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.CU; import org.apache.ignite.internal.util.typedef.internal.U; @@ -434,6 +436,16 @@ private Iterator cacheData(boolean primary, boolean backup, Affi dataStore(part).invoke(cctx, key, c); } + /** {@inheritDoc} */ + @Override public void invokeAll( + GridCacheContext cctx, + Collection keys, + GridDhtLocalPartition part, + OffheapInvokeAllClosure c) + throws IgniteCheckedException { + dataStore(part).invokeAll(cctx, keys, c); + } + /** {@inheritDoc} */ @Override public void update( GridCacheContext cctx, @@ -1607,6 +1619,20 @@ private boolean canUpdateOldRow(GridCacheContext cctx, @Nullable CacheDataRow ol } } + + /** {@inheritDoc} */ + @Override public void invokeAll(GridCacheContext cctx, Collection keys, OffheapInvokeAllClosure c) + throws IgniteCheckedException { + int cacheId = grp.sharedGroup() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; + + List searchRows = new ArrayList<>(keys.size()); + + for (KeyCacheObject key : keys) + searchRows.add(new SearchRow(cacheId, key)); + + invokeAll0(cctx, searchRows, c); + } + /** * @param cctx Cache context. * @param row Search row. @@ -1646,6 +1672,57 @@ private void invoke0(GridCacheContext cctx, CacheSearchRow row, OffheapInvokeClo } } + /** + * @param cctx Cache context. + * @param rows Search rows. + * @param c Closure. + * @throws IgniteCheckedException If failed. + */ + private void invokeAll0(GridCacheContext cctx, List rows, OffheapInvokeAllClosure c) + throws IgniteCheckedException { + if (!busyLock.enterBusy()) + throw new NodeStoppingException("Operation has been cancelled (node is stopping)."); + + try { + assert cctx.shared().database().checkpointLockIsHeldByThread(); + + dataTree.invokeAll(rows, CacheDataRowAdapter.RowData.NO_KEY, c); + + for (T3 tuple : c.result()) { + IgniteTree.OperationType opType = tuple.get1(); + CacheDataRow oldRow = tuple.get2(); + CacheDataRow newRow = tuple.get3(); + + switch (opType) { + case PUT: { + assert newRow != null : tuple; + + finishUpdate(cctx, newRow, oldRow); + + break; + } + + case REMOVE: { + finishRemove(cctx, newRow.key(), oldRow); + + break; + } + + case NOOP: + break; + + default: + assert false : opType; + } + } + + + } + finally { + busyLock.leaveBusy(); + } + } + /** {@inheritDoc} */ @Override public CacheDataRow createRow( GridCacheContext cctx, diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 61f1e06f9dccb..95aae574a06be 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; +import org.apache.ignite.IgniteSystemProperties; import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; @@ -40,6 +41,7 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.BatchedCacheEntries; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; import org.apache.ignite.internal.processors.cache.CacheGroupContext; import org.apache.ignite.internal.processors.cache.CacheMetricsImpl; @@ -86,6 +88,16 @@ * Thread pool for requesting partitions from other nodes and populating local cache. */ public class GridDhtPartitionDemander { + /** todo explain the origin */ + private static final int BATCH_PRELOAD_THRESHOLD = 5; + + /** */ + private static final int CHECKPOINT_THRESHOLD = 200; + + /** */ + private static final boolean batchPageWriteEnabled = + IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE, false); + /** */ private final GridCacheSharedContext ctx; @@ -766,10 +778,10 @@ public void handleSupplyMessage( part.lock(); try { - Iterator infos = e.getValue().infos().iterator(); + Collection infos = e.getValue().infos(); if (grp.mvccEnabled()) - mvccPreloadEntries(topVer, node, p, infos); + mvccPreloadEntries(topVer, node, p, infos.iterator()); else preloadEntries(topVer, node, p, infos); @@ -851,6 +863,116 @@ public void handleSupplyMessage( } } + /** + * todo should be removed (kept for benchamrking) + */ + public void preloadEntriesSingle(ClusterNode from, + int p, + Collection entries, + AffinityTopologyVersion topVer + ) throws IgniteCheckedException { + GridCacheContext cctx = null; + + // Loop through all received entries and try to preload them. + for (GridCacheEntryInfo entry : entries) { + if (cctx == null || (grp.sharedGroup() && entry.cacheId() != cctx.cacheId())) { + cctx = grp.sharedGroup() ? grp.shared().cacheContext(entry.cacheId()) : grp.singleCacheContext(); + + if (cctx == null) + continue; + else if (cctx.isNear()) + cctx = cctx.dhtCache().context(); + } + + if (!preloadEntry(from, p, entry, topVer, cctx)) { + if (log.isTraceEnabled()) + log.trace("Got entries for invalid partition during " + + "preloading (will skip) [p=" + p + ", entry=" + entry + ']'); + + break; + } + + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeyReceived(); + } + } + } + + /** + * @param from Node which sent entry. + * @param p Partition id. + * @param entries Preloaded entries. + * @param topVer Topology version. + * + * @throws IgniteCheckedException If failed. + */ + public void preloadEntriesBatch(ClusterNode from, + int p, + Collection entries, + AffinityTopologyVersion topVer + ) throws IgniteCheckedException { + if (entries.isEmpty()) + return; + + Map cctxMap = new HashMap<>(); + + // Map by context. + for (GridCacheEntryInfo info : entries) { + try { + GridCacheContext cctx0 = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); + + if (cctx0 == null) + return; + + if (cctx0.isNear()) + cctx0 = cctx0.dhtCache().context(); + + final GridCacheContext cctx = cctx0; + + if (log.isTraceEnabled()) + log.trace("Rebalancing key [key=" + info.key() + ", part=" + p + ", node=" + from.id() + ']'); + + BatchedCacheEntries batch = cctxMap.get(cctx.cacheId()); + + if (batch == null) { + // todo lock should be called for ALL group + cctx.group().listenerLock().readLock().lock(); + + cctxMap.put(cctx.cacheId(), batch = new BatchedCacheEntries(topVer, p, cctx, true)); + } + + batch.addEntry(info.key(), info.value(), info.expireTime(), info.ttl(), info.version(), DR_PRELOAD); + } + catch (GridDhtInvalidPartitionException ignored) { + if (log.isDebugEnabled()) + log.debug("Partition became invalid during rebalancing (will ignore): " + p); + } + } + + for (BatchedCacheEntries batch : cctxMap.values()) { + assert batch.size() > BATCH_PRELOAD_THRESHOLD : batch.size(); + + GridCacheContext cctx = batch.context(); + + batch.lock(); + + try { + cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.new BatchUpdateClosure()); + } + finally { + batch.unlock(); + + cctx.group().listenerLock().readLock().unlock(); + + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeysReceived(batch.size()); + } + } + } + } + /** * Adds mvcc entries with theirs history to partition p. * @@ -942,14 +1064,52 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node * * @param node Node which sent entry. * @param p Partition id. - * @param infos Entries info for preload. + * @param infosCol Entries info for preload. * @param topVer Topology version. * @throws IgniteInterruptedCheckedException If interrupted. */ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, - Iterator infos) throws IgniteCheckedException { + Collection infosCol) throws IgniteCheckedException { GridCacheContext cctx = null; + int size = infosCol.size(); + + boolean batchEnabled = + batchPageWriteEnabled && size > BATCH_PRELOAD_THRESHOLD; + + int nBatch = 0; + int total = size / CHECKPOINT_THRESHOLD; + + Iterator infos = infosCol.iterator(); + + // Loop through all received entries and try to preload them. + while (infos.hasNext()) { + ctx.database().checkpointReadLock(); + + boolean tail = (nBatch++ >= (total - 1)); + + try { + List infosBatch = new ArrayList<>(CHECKPOINT_THRESHOLD); + + for (int i = 0; i < (tail ? CHECKPOINT_THRESHOLD + (size % CHECKPOINT_THRESHOLD) : CHECKPOINT_THRESHOLD); i++) { + if (!infos.hasNext()) + break; + + GridCacheEntryInfo entry = infos.next(); + + infosBatch.add(entry); + } + + if (batchEnabled && infosBatch.size() > BATCH_PRELOAD_THRESHOLD) + preloadEntriesBatch(node, p, infosBatch, topVer); + else + preloadEntriesSingle(node, p, infosBatch, topVer); + } + finally { + ctx.database().checkpointReadUnlock(); + } + } + // Loop through all received entries and try to preload them. while (infos.hasNext()) { ctx.database().checkpointReadLock(); @@ -1390,7 +1550,8 @@ private void partitionDone(UUID nodeId, int p, boolean updateState) { "rebalancing [grp=" + grp.cacheOrGroupName() + ", supplier=" + nodeId + ", topVer=" + topologyVersion() + - ", progress=" + (routines - remainingRoutines) + "/" + routines + "]")); + ", progress=" + (routines - remainingRoutines) + "/" + routines + "," + + ", batch=" + batchPageWriteEnabled + "]")); remaining.remove(nodeId); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java index 35dd3c46ee431..ab9bf86913bdd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence; +import java.util.Collection; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import org.apache.ignite.IgniteCheckedException; @@ -307,6 +308,26 @@ protected final R write( return PageHandler.writePage(pageMem, grpId, pageId, this, h, init, wal, null, arg, intArg, lockFailed, statHolder); } + /** + * @param pageId Page ID. + * @param h Handler. + * @param init IO for new page initialization or {@code null} if it is an existing page. + * @param arg Argument. + * @param lockFailed Result in case of lock failure due to page recycling. + * @param statHolder Statistics holder to track IO operations. + * @return Handler result. + * @throws IgniteCheckedException If failed. + */ + protected final R write( + long pageId, + PageHandler h, + PageIO init, + Collection arg, + R lockFailed, + IoStatisticsHolder statHolder) throws IgniteCheckedException { + return PageHandler.writePageBatch(pageMem, grpId, pageId, this, h, init, wal, null, arg, lockFailed, statHolder); + } + /** * @param pageId Page ID. * @param h Handler. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java index ab48540f6dfc5..1c2cbedf11452 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/GridCacheOffheapManager.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -1990,7 +1991,7 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { return delegate.mvccInitialValue(cctx, key, val, ver, expireTime, mvccVer, newMvccVer); } - + /** {@inheritDoc} */ @Override public boolean mvccApplyHistoryIfAbsent( GridCacheContext cctx, @@ -2130,6 +2131,16 @@ private Metas getOrAllocatePartitionMetas() throws IgniteCheckedException { delegate.invoke(cctx, key, c); } + /** {@inheritDoc} */ + @Override public void invokeAll(GridCacheContext cctx, Collection keys, OffheapInvokeAllClosure c) + throws IgniteCheckedException { + assert ctx.database().checkpointLockIsHeldByThread(); + + CacheDataStore delegate = init0(false); + + delegate.invokeAll(cctx, keys, c); + } + /** {@inheritDoc} */ @Override public void remove(GridCacheContext cctx, KeyCacheObject key, int partId) throws IgniteCheckedException { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java index ee19aed7284b3..f7afa694ac5c0 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/IgniteCacheDatabaseSharedManager.java @@ -250,7 +250,7 @@ protected void initPageMemoryDataStructures(DataStorageConfiguration dbCfg) thro boolean persistenceEnabled = memPlcCfg.isPersistenceEnabled(); CacheFreeListImpl freeList = new CacheFreeListImpl(0, - cctx.igniteInstanceName(), + memPlc.config().getName(), memMetrics, memPlc, null, diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index 91fd2070cc048..2f2942d51ce1d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence; +import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.processors.cache.CacheGroupContext; @@ -111,6 +112,25 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit } } + /** + * @param rows Rows. + * @throws IgniteCheckedException If failed. + */ + public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { + if (!persistenceEnabled) + freeList.insertDataRows(rows, statHolder); + else { + ctx.database().checkpointReadLock(); + + try { + freeList.insertDataRows(rows, statHolder); + } + finally { + ctx.database().checkpointReadUnlock(); + } + } + } + /** * @param link Row link. * @param row New row data. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 60aefb927ce6f..7a3fefc6010f0 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -17,6 +17,9 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.concurrent.atomic.AtomicReferenceArray; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; @@ -30,6 +33,7 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageUpdateRecord; import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl; +import org.apache.ignite.internal.processors.cache.persistence.IndexStorageImpl; import org.apache.ignite.internal.processors.cache.persistence.Storable; import org.apache.ignite.internal.processors.cache.persistence.evict.PageEvictionTracker; import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO; @@ -133,12 +137,15 @@ private final class UpdateRowHandler extends PageHandler { /** */ private final PageHandler writeRow = new WriteRowHandler(); + /** */ + private final PageHandler writeRows = new WriteRowHandlerBatch(); + /** * */ - private final class WriteRowHandler extends PageHandler { - @Override public Integer run( - int cacheId, + private class WriteRowHandler extends PageHandler { + /** {@inheritDoc} */ + @Override public Integer run(int cacheId, long pageId, long page, long pageAddr, @@ -146,6 +153,33 @@ private final class WriteRowHandler extends PageHandler { Boolean walPlc, T row, int written, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + written = run0(pageId, page, pageAddr, iox, row, written, statHolder); + + putPage((AbstractDataPageIO)iox, pageId, page, pageAddr, statHolder); + + return written; + } + + /** + * @param pageId Page ID. + * @param page Page absolute pointer. + * @param pageAddr Page address. + * @param iox IO. + * @param row Data row. + * @param written Count of bytes written. + * @param statHolder Statistics holder to track IO operations. + * @return Result. + * @throws IgniteCheckedException If failed. + */ + protected Integer run0( + long pageId, + long page, + long pageAddr, + PageIO iox, + T row, + int written, IoStatisticsHolder statHolder) throws IgniteCheckedException { AbstractDataPageIO io = (AbstractDataPageIO)iox; @@ -159,15 +193,6 @@ private final class WriteRowHandler extends PageHandler { written = (written == 0 && oldFreeSpace >= rowSize) ? addRow(pageId, page, pageAddr, io, row, rowSize) : addRowFragment(pageId, page, pageAddr, io, row, written, rowSize); - // Reread free space after update. - int newFreeSpace = io.getFreeSpace(pageAddr); - - if (newFreeSpace > MIN_PAGE_FREE_SPACE) { - int bucket = bucket(newFreeSpace, false); - - put(null, pageId, page, pageAddr, bucket, statHolder); - } - if (written == rowSize) evictionTracker.touchPage(pageId); @@ -185,7 +210,7 @@ private final class WriteRowHandler extends PageHandler { * @return Written size which is always equal to row size here. * @throws IgniteCheckedException If failed. */ - private int addRow( + protected int addRow( long pageId, long page, long pageAddr, @@ -225,7 +250,7 @@ private int addRow( * @return Updated written size. * @throws IgniteCheckedException If failed. */ - private int addRowFragment( + protected int addRowFragment( long pageId, long page, long pageAddr, @@ -254,6 +279,83 @@ private int addRowFragment( return written + payloadSize; } + + /** + * Put page to freelist if needed. + * + * @param iox IO. + * @param pageId Page ID. + * @param page Paege pointer. + * @param pageAddr Page address. + * @param statHolder Statistics holder to track IO operations. + */ + protected void putPage( + AbstractDataPageIO iox, + long pageId, + long page, + long pageAddr, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + // Reread free space after update. + int newFreeSpace = ((AbstractDataPageIO)iox).getFreeSpace(pageAddr); + + if (newFreeSpace > MIN_PAGE_FREE_SPACE) { + int bucket = bucket(newFreeSpace, false); + + put(null, pageId, page, pageAddr, bucket, statHolder); + } + } + } + + /** + * + */ + private class WriteRowHandlerBatch extends WriteRowHandler { + /** {@inheritDoc} */ + @Override public Integer runBatch( + int cacheId, + long pageId, + long page, + long pageAddr, + PageIO io, + Boolean walPlc, + Collection args, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + int maxPayloadSize = pageSize() - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; + + AbstractDataPageIO iox = (AbstractDataPageIO)io; + + // todo !! DO NOT FORGET WAL DELTA !! + if (iox.getFreeSpace(pageAddr) == maxPayloadSize) { + // todo save links for WAL + + iox.addRows(pageMem, pageId, pageAddr, args, pageSize()); + + // todo update wal + } + else { + for (T row : args) { + assert iox.getFreeSpace(pageAddr) > 0 : iox.getFreeSpace(pageAddr); + + int size = row.size(); + + int written = size > maxPayloadSize ? + addRowFragment(pageId, page, pageAddr, iox, row, size - (size % maxPayloadSize), size) : + addRow(pageId, page, pageAddr, iox, row, size); + + assert written == size : "The object is not fully written into page: " + + "pageId=" + pageId + ", written=" + written + ", size=" + row.size(); + + evictionTracker.touchPage(pageId); + } + } + + // return page to freelist if needed + putPage((AbstractDataPageIO)io, pageId, page, pageAddr, statHolder); + + return COMPLETE; + } } /** */ @@ -509,6 +611,155 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) while (written != COMPLETE); } + /** {@inheritDoc} */ + @Override public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { + // 1. split into 3 bags + // A. Large objects. + // B1. Tails of large objects + // B2. small objects + + // Max bytes per data page. + int maxPayloadSize = pageSize() - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; + + int maxRowsPerPage = IndexStorageImpl.MAX_IDX_NAME_LEN; + + // Data rows <-> count of pages needed + List largeRows = new ArrayList<>(16); + + // other objects + List regularRows = new ArrayList<>(16); + + for (T dataRow : rows) { + if (dataRow.size() < maxPayloadSize) + regularRows.add(dataRow); + else { + largeRows.add(dataRow); + + int tailSize = dataRow.size() % maxPayloadSize; + + if (tailSize > 0) + regularRows.add(dataRow); + } + } + + // Writing large objects. + for (T row : largeRows) { + int rowSize = row.size(); + + int written = 0; + + do { + if (written != 0) + memMetrics.incrementLargeEntriesPages(); + + int remaining = rowSize - written; + + long pageId; + + if (remaining >= MIN_SIZE_FOR_DATA_PAGE) + pageId = takeEmptyPage(REUSE_BUCKET, ioVersions(), statHolder); + else + break; + + AbstractDataPageIO initIo = null; + + if (pageId == 0L) { + pageId = allocateDataPage(row.partition()); + + initIo = ioVersions().latest(); + } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) + pageId = initReusedPage(pageId, row.partition(), statHolder); + else + pageId = PageIdUtils.changePartitionId(pageId, (row.partition())); + + written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); + + assert written != FAIL_I; // We can't fail here. + } + while (written != COMPLETE); + } + + List dataRows = new ArrayList<>(maxRowsPerPage); + + int remainPageSpace = 0; + + long pageId = 0; + + AbstractDataPageIO initIo = null; + + for (int i = 0; i < regularRows.size(); i++) { + T row = regularRows.get(i); + + boolean tail = i == (regularRows.size() - 1); + + boolean fragment = row.size() > maxPayloadSize; + + int payloadSize = fragment ? (row.size() % maxPayloadSize) + 12 : row.size() + 4; + + // There is no space left on this page. + if (((remainPageSpace - payloadSize) < 0 || dataRows.size() == maxRowsPerPage) && pageId != 0) { + int written = write(pageId, writeRows, initIo, dataRows, FAIL_I, statHolder); + + assert written == COMPLETE : written; + + initIo = null; + remainPageSpace = 0; + pageId = 0; + dataRows.clear(); + } + + dataRows.add(row); + + if (pageId == 0) { + int minBucket = bucket(payloadSize, false) + 1; + + if (payloadSize != MIN_SIZE_FOR_DATA_PAGE) { + for (int b = REUSE_BUCKET - 1; b >= minBucket; b--) { + pageId = takeEmptyPage(b, ioVersions(), statHolder); + + if (pageId != 0L) { + remainPageSpace = (b << shift); //todo + 4, wtf "+4"? + + break; + } + } + } + + if (pageId == 0) + pageId = takeEmptyPage(REUSE_BUCKET, ioVersions(), statHolder); + + if (pageId == 0) { + pageId = allocateDataPage(row.partition()); + + initIo = ioVersions().latest(); + } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) + pageId = initReusedPage(pageId, row.partition(), statHolder); + else + pageId = PageIdUtils.changePartitionId(pageId, row.partition()); + + if (remainPageSpace == 0) + remainPageSpace = maxPayloadSize; + } + + remainPageSpace -= payloadSize; + + if (tail) { + int written; + + if (dataRows.size() == 1) { + written = fragment ? row.size() - (row.size() % maxPayloadSize) : 0; + + written = write(pageId, writeRows, initIo, row, written, FAIL_I, statHolder); + } else + written = write(pageId, writeRows, initIo, dataRows, FAIL_I, statHolder); + + assert written == COMPLETE : written; + } + } + } + /** * @param reusedPageId Reused page id. * @param partId Partition id. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java index e28d421bdf063..894c1aa64faca 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/FreeList.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence.freelist; +import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.processors.cache.persistence.Storable; @@ -32,6 +33,12 @@ public interface FreeList { */ public void insertDataRow(T row, IoStatisticsHolder statHolder) throws IgniteCheckedException; + /** + * @param rows Rows. + * @throws IgniteCheckedException If failed. + */ + public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException; + /** * @param link Row link. * @param row New row data. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java index 54d9816bcb7e2..7ab4dbe5345aa 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java @@ -1822,6 +1822,11 @@ public final boolean removex(L row) throws IgniteCheckedException { } } + /** {@inheritDoc} */ + @Override public void invokeAll(List keys, Object z, InvokeAllClosure c) throws IgniteCheckedException { + throw new UnsupportedOperationException("Not implemented yet"); + } + /** * @param x Invoke operation. * @param pageId Page ID. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java index 78752bbfefc84..4b2d4030fe581 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java @@ -31,6 +31,7 @@ import org.apache.ignite.internal.processors.cache.persistence.tree.util.PageHandler; import org.apache.ignite.internal.util.GridStringBuilder; import org.apache.ignite.internal.util.typedef.internal.SB; +import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.util.GridUnsafe.bufferAddress; @@ -810,7 +811,7 @@ public void addRow( final int rowSize, final int pageSize ) throws IgniteCheckedException { - assert rowSize <= getFreeSpace(pageAddr) : "can't call addRow if not enough space for the whole row"; + assert rowSize <= getFreeSpace(pageAddr) : "can't call addRow if not enough space for the whole row (free=" + getFreeSpace(pageAddr) + ", required=" + rowSize + ")"; int fullEntrySize = getPageEntrySize(rowSize, SHOW_PAYLOAD_LEN | SHOW_ITEM); @@ -977,6 +978,75 @@ public void addRowFragment( addRowFragment(null, pageId, pageAddr, 0, 0, lastLink, null, payload, pageSize); } + /** + * @param pageMem Page memory. + * @param pageId Page ID to use to construct a link. + * @param pageAddr Page address. + * @param rows Data rows. + * @param pageSize Page size. + * @throws IgniteCheckedException If failed. + */ + public void addRows( + final PageMemory pageMem, + final long pageId, + final long pageAddr, + final Collection rows, + final int pageSize + ) throws IgniteCheckedException { + // todo code duplication (3 times!) + int maxPayloadSIze = pageSize - MIN_DATA_PAGE_OVERHEAD; + int dataOff = pageSize; + int cnt = 0; + int written = 0; + + for (T row : rows) { + boolean fragment = row.size() > maxPayloadSIze; + + int payloadSize = row.size() % maxPayloadSIze; + + assert payloadSize <= getFreeSpace(pageAddr) : "can't call addRow if not enough space for the whole row"; + + int sizeSetup = fragment ? SHOW_PAYLOAD_LEN | SHOW_LINK | SHOW_ITEM : SHOW_PAYLOAD_LEN | SHOW_ITEM; + + int fullEntrySize = getPageEntrySize(payloadSize, sizeSetup); + + written += fullEntrySize; + + dataOff -= (fullEntrySize - ITEM_SIZE); + + if (fragment) { + ByteBuffer buf = pageMem.pageBuffer(pageAddr); + + buf.position(dataOff); + + buf.putShort((short)(payloadSize | FRAGMENTED_FLAG)); + buf.putLong(row.link()); + + // todo is it 0? + writeFragmentData(row, buf, 0, payloadSize); + } + else + writeRowData(pageAddr, dataOff, payloadSize, row, true); + + setItem(pageAddr, cnt, directItemFromOffset(dataOff)); + + assert checkIndex(cnt) : cnt; + assert getIndirectCount(pageAddr) <= getDirectCount(pageAddr); + + setLinkByPageId(row, pageId, cnt); + + ++cnt; + } + + setDirectCount(pageAddr, cnt); + + setFirstEntryOffset(pageAddr, dataOff, pageSize); + + // Update free space. If number of indirect items changed, then we were able to reuse an item slot. + // + (getIndirectCount(pageAddr) != indirectCnt ? ITEM_SIZE : 0) + setRealFreeSpace(pageAddr, getRealFreeSpace(pageAddr) - written, pageSize); + } + /** * Adds maximum possible fragment of the given row to this data page and sets respective link to the row. * @@ -1112,6 +1182,7 @@ private int insertItem(long pageAddr, int dataOff, int directCnt, int indirectCn setItem(pageAddr, directCnt, directItemFromOffset(dataOff)); setDirectCount(pageAddr, directCnt + 1); + assert getDirectCount(pageAddr) == directCnt + 1; return directCnt; // Previous directCnt will be our itemId. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java index 5ab1bf38dbc18..72302bf36ecad 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.cache.persistence.tree.util; import java.nio.ByteBuffer; +import java.util.Collection; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.pagemem.PageSupport; @@ -70,6 +71,32 @@ public abstract R run( ) throws IgniteCheckedException; + /** + * @param cacheId Cache ID. + * @param pageId Page ID. + * @param page Page absolute pointer. + * @param pageAddr Page address. + * @param io IO. + * @param walPlc Full page WAL record policy. + * @param args Arguments. + * @param statHolder Statistics holder to track IO operations. + * @return Result. + * @throws IgniteCheckedException If failed. + */ + public R runBatch( + int cacheId, + long pageId, + long page, + long pageAddr, + PageIO io, + Boolean walPlc, + Collection args, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + // todo + throw new UnsupportedOperationException(); + } + /** * @param cacheId Cache ID. * @param pageId Page ID. @@ -308,6 +335,74 @@ public static R writePage( } } + /** + * @param pageMem Page memory. + * @param grpId Group ID. + * @param pageId Page ID. + * @param lsnr Lock listener. + * @param h Handler. + * @param init IO for new page initialization or {@code null} if it is an existing page. + * @param wal Write ahead log. + * @param walPlc Full page WAL record policy. + * @param args Argument. + * @param lockFailed Result in case of lock failure due to page recycling. + * @param statHolder Statistics holder to track IO operations. + * @return Handler result. + * @throws IgniteCheckedException If failed. + */ + public static R writePageBatch( + PageMemory pageMem, + int grpId, + final long pageId, + PageLockListener lsnr, + PageHandler h, + PageIO init, + IgniteWriteAheadLogManager wal, + Boolean walPlc, + Collection args, + R lockFailed, + IoStatisticsHolder statHolder + ) throws IgniteCheckedException { + boolean releaseAfterWrite = true; + + long page = pageMem.acquirePage(grpId, pageId, statHolder); + + try { + long pageAddr = writeLock(pageMem, grpId, pageId, page, lsnr, false); + + if (pageAddr == 0L) + return lockFailed; + + boolean ok = false; + + try { + if (init != null) { + // It is a new page and we have to initialize it. + doInitPage(pageMem, grpId, pageId, page, pageAddr, init, wal); + walPlc = FALSE; + } + else + init = PageIO.getPageIO(pageAddr); + + R res = h.runBatch(grpId, pageId, page, pageAddr, init, walPlc, args, statHolder); + + ok = true; + + return res; + } + finally { + assert PageIO.getCrc(pageAddr) == 0; //TODO GG-11480 + + if (releaseAfterWrite = h.releaseAfterWrite(grpId, pageId, page, pageAddr, null, 0)) + writeUnlock(pageMem, grpId, pageId, page, pageAddr, lsnr, walPlc, ok); + } + } + finally { + if (releaseAfterWrite) + pageMem.releasePage(grpId, pageId, page); + } + } + /** * @param pageMem Page memory. * @param grpId Group ID. diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java index b3c1c69e66319..7546f0569c3dc 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java @@ -17,6 +17,9 @@ package org.apache.ignite.internal.processors.cache.tree; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageUtils; import org.apache.ignite.internal.pagemem.store.PageStore; @@ -45,6 +48,8 @@ import org.apache.ignite.internal.stat.IoStatisticsHolder; import org.apache.ignite.internal.util.GridUnsafe; import org.apache.ignite.internal.util.lang.GridCursor; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.CU; import static java.lang.Boolean.FALSE; @@ -327,6 +332,87 @@ public CacheDataRowStore rowStore() { return rowStore; } + /** + * todo fake implementation only for checking that closure is working properly with preloader. + * @param keys Keys. + * @param x Implementation specific argument, {@code null} always means that we need a full detached data row. + * @param c Closure. + * @throws IgniteCheckedException If failed. + */ + @Override public void invokeAll(List keys, Object x, InvokeAllClosure c) throws IgniteCheckedException { + checkDestroyed(); + + int cnt = keys.size(); + + assert cnt > 0 : cnt; + + CacheSearchRow lower = keys.get(0); + CacheSearchRow upper = keys.get(cnt - 1); + + List> batch = new ArrayList<>(cnt); + + Iterator rowItr = keys.iterator(); + + assert lower.key().hashCode() <= upper.key().hashCode() : "Keys must be lower=" + lower.key().hashCode() + ", upper=" + upper.key().hashCode(); + + GridCursor cur = find(lower, upper, CacheDataRowAdapter.RowData.FULL); + + CacheSearchRow lastSearchRow = null; + KeyCacheObject newKey = null; + + while (cur.next()) { + CacheDataRow oldRow = cur.get(); + KeyCacheObject oldKey = oldRow.key(); + + while (newKey == null || newKey.hashCode() <= oldKey.hashCode()) { + if (newKey != null && newKey.hashCode() == oldKey.hashCode()) { + while (newKey.hashCode() == oldKey.hashCode()) { + if (newKey.equals(oldKey)) + batch.add(new T2<>(oldRow, lastSearchRow)); + else + batch.add(new T2<>(null, lastSearchRow)); + + if (!rowItr.hasNext()) + break; + + lastSearchRow = rowItr.next(); + newKey = lastSearchRow.key(); + } + } + else { + if (lastSearchRow != null) + batch.add(new T2<>(null, lastSearchRow)); + + if (!rowItr.hasNext()) + break; + + lastSearchRow = rowItr.next(); + newKey = lastSearchRow.key(); + } + + if (!rowItr.hasNext()) + break; + } + } + + while (rowItr.hasNext()) + batch.add(new T2<>(null, rowItr.next())); + + c.call(batch); + + for (T3 t3 : c.result()) { + OperationType oper = t3.get1(); + CacheDataRow oldRow = t3.get2(); + CacheDataRow newRow = t3.get3(); + + if (oper == OperationType.PUT) + put(newRow); + else + if (oper == OperationType.REMOVE) + remove(oldRow); + } + } + /** {@inheritDoc} */ @Override protected int compare(BPlusIO iox, long pageAddr, int idx, CacheSearchRow row) throws IgniteCheckedException { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java index 9e854d28f6cb0..12d1a6d3918dc 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java @@ -17,8 +17,12 @@ package org.apache.ignite.internal.util; +import java.util.Collection; +import java.util.List; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.util.lang.GridCursor; +import org.apache.ignite.internal.util.typedef.T2; +import org.apache.ignite.internal.util.typedef.T3; import org.jetbrains.annotations.Nullable; /** @@ -42,6 +46,14 @@ public interface IgniteTree { */ public void invoke(L key, Object x, InvokeClosure c) throws IgniteCheckedException; + /** + * @param keys Keys. + * @param x Implementation specific argument, {@code null} always means that we need a full detached data row. + * @param c Closure. + * @throws IgniteCheckedException If failed. + */ + public void invokeAll(List keys, Object x, InvokeAllClosure c) throws IgniteCheckedException; + /** * Returns the value to which the specified key is mapped, or {@code null} if this tree contains no mapping for the * key. @@ -130,6 +142,25 @@ interface InvokeClosure { OperationType operationType(); } + /** + * T found row + * L search row + */ + interface InvokeAllClosure { + /** + * + * @param rows Old row -> new row + * @throws IgniteCheckedException If failed. + */ + void call(@Nullable Collection> rows) throws IgniteCheckedException; + + /** + * + * @return operation, old row, new row + */ + Collection> result(); + } + /** * */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java new file mode 100644 index 0000000000000..2458cfd375d6e --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java @@ -0,0 +1,342 @@ +/* + * 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.database; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; +import org.apache.ignite.Ignite; +import org.apache.ignite.IgniteCache; +import org.apache.ignite.IgniteDataStreamer; +import org.apache.ignite.cache.CacheAtomicityMode; +import org.apache.ignite.cache.CacheMode; +import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction; +import org.apache.ignite.cluster.BaselineNode; +import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataRegionConfiguration; +import org.apache.ignite.configuration.DataStorageConfiguration; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.configuration.WALMode; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.IgniteInterruptedCheckedException; +import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition; +import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtPartitionState; +import org.apache.ignite.internal.util.typedef.PA; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.testframework.GridTestUtils; +import org.apache.ignite.testframework.junits.WithSystemProperty; +import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.apache.ignite.IgniteSystemProperties.IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE; +import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_WAL_REBALANCE_THRESHOLD; +import static org.junit.Assert.assertArrayEquals; + +/** + * + */ +@RunWith(Parameterized.class) +public class FreeListPreloadWithBatchUpdatesTest extends GridCommonAbstractTest { + /** */ + private static final int HDR_SIZE = 8 + 32; + + /** */ + private static final long DEF_REG_SIZE_INIT = 3400 * 1024 * 1024L; + + /** */ + private static final long DEF_REG_SIZE = 6144 * 1024 * 1024L; + + /** */ + private static final String DEF_CACHE_NAME = "some-cache"; + + /** */ + @Parameterized.Parameters(name = "with atomicity={0} and persistence={1}") + public static Iterable setup() { + return Arrays.asList(new Object[][]{ + {CacheAtomicityMode.ATOMIC, false}, + {CacheAtomicityMode.ATOMIC, true}, + {CacheAtomicityMode.TRANSACTIONAL, false}, + {CacheAtomicityMode.TRANSACTIONAL, true}, + {CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT, false}, + {CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT, true} + }); + } + + /** */ + @Parameterized.Parameter() + public CacheAtomicityMode cacheAtomicityMode; + + /** */ + @Parameterized.Parameter(1) + public boolean persistence; + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); + + DataRegionConfiguration def = new DataRegionConfiguration(); + def.setInitialSize(DEF_REG_SIZE_INIT); + def.setMaxSize(DEF_REG_SIZE); + def.setPersistenceEnabled(persistence); + + DataStorageConfiguration storeCfg = new DataStorageConfiguration(); + + storeCfg.setDefaultDataRegionConfiguration(def); + + if (persistence) { + storeCfg.setWalMode(WALMode.LOG_ONLY); + storeCfg.setMaxWalArchiveSize(Integer.MAX_VALUE); + } + + cfg.setDataStorageConfiguration(storeCfg); + + return cfg; + } + + /** + * + */ + @Before + public void before() throws Exception { + cleanPersistenceDir(); + } + + /** + * + */ + @After + public void after() throws Exception { + stopAllGrids(); + + cleanPersistenceDir(); + } + + /** + * + */ + @Test + @WithSystemProperty(key = IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE, value = "true") + public void testBatchRebalance() throws Exception { + Ignite node = startGrid(0); + + node.cluster().active(true); + + node.cluster().baselineAutoAdjustEnabled(false); + + node.createCache(ccfg()); + + int cnt = 100_000; + + int minSize = 0; + int maxSize = 16384; + + Map srcMap = new HashMap<>(); + + for (int i = 0; i < cnt; i++) { + int size = maxSize == minSize ? maxSize : minSize + ThreadLocalRandom.current().nextInt(maxSize - minSize); + + byte[] obj = new byte[size]; + + srcMap.put(i, obj); + } + + try (IgniteDataStreamer streamer = node.dataStreamer(DEF_CACHE_NAME)) { + streamer.addData(srcMap); + } + + log.info("Data loaded."); + + if (persistence) + node.cluster().active(false); + + final IgniteEx node2 = startGrid(1); + + if (persistence) { + List list = new ArrayList<>(node.cluster().currentBaselineTopology()); + + list.add(node2.localNode()); + + node.cluster().active(true); + + node.cluster().setBaselineTopology(list); + } + + log.info("Await rebalance."); + + awaitRebalance(node2, DEF_CACHE_NAME); + + node.close(); + + validateCacheEntries(node2.cache(DEF_CACHE_NAME), srcMap); + + if (persistence) { + node2.close(); + + Ignite ignite = startGrid(1); + + ignite.cluster().active(true); + + log.info("Validate entries after restart"); + + validateCacheEntries(ignite.cache(DEF_CACHE_NAME), srcMap); + } + } + + /** + * + */ + @Test + @WithSystemProperty(key = IGNITE_PDS_WAL_REBALANCE_THRESHOLD, value = "100") + @WithSystemProperty(key = IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE, value = "true") + public void testBatchHistoricalRebalance() throws Exception { + if (!persistence) + return; + + // TODO https://issues.apache.org/jira/browse/IGNITE-7384 + // TODO http://apache-ignite-developers.2346864.n4.nabble.com/Historical-rebalance-td38380.html + if (cacheAtomicityMode == CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT) + return; + + Ignite node = startGrids(2); + + node.cluster().active(true); + + IgniteCache cache = node.createCache(ccfg()); + + int cnt = 10_000; + + log.info("Loading " + cnt + " random entries."); + + Map srcMap = new HashMap<>(); + + for (int i = 0; i < cnt; i++) { + byte[] obj = new byte[ThreadLocalRandom.current().nextInt(16384)]; + + srcMap.put(i, obj); + } + + try (IgniteDataStreamer streamer = node.dataStreamer(DEF_CACHE_NAME)) { + streamer.addData(srcMap); + } + + forceCheckpoint(); + + log.info("Stopping node #2."); + + grid(1).close(); + + log.info("Updating values on node #1."); + + for (int i = 100; i < 1000; i++) { + if (i % 33 == 0) { + cache.remove(i); + + srcMap.remove(i); + } + else { + byte[] bytes = new byte[512]; + + Arrays.fill(bytes, (byte)1); + + srcMap.put(i, bytes); + cache.put(i, bytes); + } + } + + forceCheckpoint(); + + log.info("Starting node #2."); + + IgniteEx node2 = startGrid(1); + + log.info("Await rebalance on node #2."); + + awaitRebalance(node2, DEF_CACHE_NAME); + + log.info("Stop node #1."); + + node.close(); + + validateCacheEntries(node2.cache(DEF_CACHE_NAME), srcMap); + } + + /** + * @param node Ignite node. + * @param name Cache name. + */ + private void awaitRebalance(IgniteEx node, String name) throws IgniteInterruptedCheckedException { + boolean ok = GridTestUtils.waitForCondition(new PA() { + @Override public boolean apply() { + for ( GridDhtLocalPartition part : node.context().cache().cache(name).context().group().topology().localPartitions()) { + if (part.state() != GridDhtPartitionState.OWNING) + return false; + } + + return true; + } + }, 60_000); + + U.sleep(3000); + + assertTrue(ok); + } + + /** + * @param cache Cache. + * @param map Map. + */ + @SuppressWarnings("unchecked") + private void validateCacheEntries(IgniteCache cache, Map map) { + int size = cache.size(); + + assertEquals("Cache size mismatch.", map.size(), size); + + log.info("Validation " + cache.getName() + ", size=" + size); + + for (Map.Entry e : map.entrySet()) { + String idx = "key=" + e.getKey(); + + assertEquals(idx, e.getValue().length, ((byte[])cache.get(e.getKey())).length); + } + } + + /** + * @return Cache configuration. + */ + private CacheConfiguration ccfg() { + return ccfg(1, CacheMode.REPLICATED); + } + + /** + * @return Cache configuration. + */ + private CacheConfiguration ccfg(int parts, CacheMode mode) { + return new CacheConfiguration(DEF_CACHE_NAME) + .setAffinity(new RendezvousAffinityFunction(false, parts)) + .setCacheMode(mode) + .setAtomicityMode(cacheAtomicityMode); + } +} + + From e9025da81e61bf29508e28ff50bc3eca4e7e5dde Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 18 Mar 2019 20:45:03 +0300 Subject: [PATCH 02/18] (wip) minor. --- .../processors/cache/BatchedCacheEntries.java | 3 +++ .../dht/preloader/GridDhtPartitionDemander.java | 3 +-- .../persistence/freelist/AbstractFreeList.java | 13 ++++++------- .../cache/persistence/tree/util/PageHandler.java | 7 +++---- .../FreeListPreloadWithBatchUpdatesTest.java | 2 -- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java index b8b2e3590922f..433df696d07ad 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java @@ -264,6 +264,9 @@ private void unlockEntries(Collection locked, AffinityTopolog /** */ public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvokeAllClosure { + /** */ + private static final long serialVersionUID = -4782459128689696534L; + /** */ private final List> resBatch = new ArrayList<>(entries.size()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 95aae574a06be..2b21470a9b0b2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -864,7 +864,7 @@ public void handleSupplyMessage( } /** - * todo should be removed (kept for benchamrking) + * todo should be removed (kept for benchmarking) */ public void preloadEntriesSingle(ClusterNode from, int p, @@ -904,7 +904,6 @@ else if (cctx.isNear()) * @param p Partition id. * @param entries Preloaded entries. * @param topVer Topology version. - * * @throws IgniteCheckedException If failed. */ public void preloadEntriesBatch(ClusterNode from, diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 7a3fefc6010f0..6578e8e9c0691 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -138,7 +138,7 @@ private final class UpdateRowHandler extends PageHandler { private final PageHandler writeRow = new WriteRowHandler(); /** */ - private final PageHandler writeRows = new WriteRowHandlerBatch(); + private final PageHandler writeRows = new WriteRowsHandler(); /** * @@ -281,7 +281,7 @@ protected int addRowFragment( } /** - * Put page to freelist if needed. + * Put page into freelist if needed. * * @param iox IO. * @param pageId Page ID. @@ -310,9 +310,9 @@ protected void putPage( /** * */ - private class WriteRowHandlerBatch extends WriteRowHandler { + private class WriteRowsHandler extends WriteRowHandler { /** {@inheritDoc} */ - @Override public Integer runBatch( + @Override public Integer runAll( int cacheId, long pageId, long page, @@ -344,14 +344,13 @@ private class WriteRowHandlerBatch extends WriteRowHandler { addRowFragment(pageId, page, pageAddr, iox, row, size - (size % maxPayloadSize), size) : addRow(pageId, page, pageAddr, iox, row, size); - assert written == size : "The object is not fully written into page: " + - "pageId=" + pageId + ", written=" + written + ", size=" + row.size(); + assert written == size : "The object is not fully written into page: pageId=" + pageId + + ", written=" + written + ", size=" + row.size(); evictionTracker.touchPage(pageId); } } - // return page to freelist if needed putPage((AbstractDataPageIO)io, pageId, page, pageAddr, statHolder); return COMPLETE; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java index 72302bf36ecad..c5e6bda0dadd9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java @@ -83,7 +83,7 @@ public abstract R run( * @return Result. * @throws IgniteCheckedException If failed. */ - public R runBatch( + public R runAll( int cacheId, long pageId, long page, @@ -93,8 +93,7 @@ public R runBatch( Collection args, IoStatisticsHolder statHolder ) throws IgniteCheckedException { - // todo - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(); } /** @@ -384,7 +383,7 @@ public static R writePageBatch( else init = PageIO.getPageIO(pageAddr); - R res = h.runBatch(grpId, pageId, page, pageAddr, init, walPlc, args, statHolder); + R res = h.runAll(grpId, pageId, page, pageAddr, init, walPlc, args, statHolder); ok = true; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java index 2458cfd375d6e..c4f6c801224b8 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java @@ -338,5 +338,3 @@ private CacheConfiguration ccfg(int parts, CacheMode mode) { .setAtomicityMode(cacheAtomicityMode); } } - - From 6f5f48790b64db47cade672304ee0798cbc3a1df Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 19 Mar 2019 14:08:23 +0300 Subject: [PATCH 03/18] IGNITE-7935 Cleanup. --- ...CacheEntries.java => CacheMapEntries.java} | 182 ++++++++---------- .../preloader/GridDhtPartitionDemander.java | 75 ++------ 2 files changed, 97 insertions(+), 160 deletions(-) rename modules/core/src/main/java/org/apache/ignite/internal/processors/cache/{BatchedCacheEntries.java => CacheMapEntries.java} (72%) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java similarity index 72% rename from modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java rename to modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index 433df696d07ad..e6c566ca69953 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/BatchedCacheEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -19,13 +19,15 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; +import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager.OffheapInvokeAllClosure; import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; -import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtInvalidPartitionException; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow; @@ -36,7 +38,6 @@ import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.CU; -import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.processors.cache.GridCacheMapEntry.ATOMIC_VER_COMPARATOR; @@ -45,18 +46,15 @@ import static org.apache.ignite.internal.util.IgniteTree.OperationType.REMOVE; /** - * Batch of cache entries to optimize page memory processing. + * Batch of cache map entries. */ -public class BatchedCacheEntries { +public class CacheMapEntries { /** */ private final GridDhtLocalPartition part; /** */ private final GridCacheContext cctx; - /** */ - private final LinkedHashMap infos = new LinkedHashMap<>(); - /** */ private final AffinityTopologyVersion topVer; @@ -64,13 +62,13 @@ public class BatchedCacheEntries { private final boolean preload; /** */ - private List entries; + private final LinkedHashMap infos = new LinkedHashMap<>(); /** */ - private int skipped; + private Set skipped = new HashSet<>(); /** */ - public BatchedCacheEntries(AffinityTopologyVersion topVer, int partId, GridCacheContext cctx, boolean preload) { + public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheContext cctx, boolean preload) { this.topVer = topVer; this.cctx = cctx; this.preload = preload; @@ -79,8 +77,7 @@ public BatchedCacheEntries(AffinityTopologyVersion topVer, int partId, GridCache /** */ public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { - // todo remove `key` duplication (Map lock() { - return entries = lockEntries(infos.values(), topVer); - } - - /** */ - public void unlock() { - unlockEntries(infos.values(), topVer); + return skipped.contains(key); } /** */ - public int size() { - return infos.size() - skipped; - } - - /** */ - private List lockEntries(Collection list, AffinityTopologyVersion topVer) - throws GridDhtInvalidPartitionException { - List locked = new ArrayList<>(list.size()); + public void lock() { + List locked = new ArrayList<>(infos.size()); while (true) { - for (CacheMapEntryInfo info : list) { - GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(info.key(), topVer); + for (Map.Entry e : infos.entrySet()) { + GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(e.getKey(), topVer); locked.add(entry); - info.cacheEntry(entry); + e.getValue().cacheEntry(entry); } boolean retry = false; @@ -191,43 +169,36 @@ private List lockEntries(Collection list, } if (!retry) - return locked; + return; } } /** - * Releases java-level locks on cache entries - * todo carefully think about possible reorderings in locking/unlocking. - * - * @param locked Locked entries. - * @param topVer Topology version. + * Releases java-level locks on cache entries. */ - private void unlockEntries(Collection locked, AffinityTopologyVersion topVer) { + public void unlock() { // Process deleted entries before locks release. assert cctx.deferredDelete() : this; // Entries to skip eviction manager notification for. // Enqueue entries while holding locks. - // todo Common skip list. - Collection skip = null; - - int size = locked.size(); + int size = infos.size(); try { - for (CacheMapEntryInfo info : locked) { - GridCacheMapEntry entry = info.cacheEntry(); + for (Map.Entry e : infos.entrySet()) { + KeyCacheObject key = e.getKey(); + GridCacheMapEntry entry = e.getValue().cacheEntry(); - if (entry != null && entry.deleted()) { - if (skip == null) - skip = U.newHashSet(locked.size()); + if (skipped.contains(key)) + continue; - skip.add(entry.key()); - } + if (entry != null && entry.deleted()) + skipped.add(entry.key()); try { - info.updateCacheEntry(); - } catch (IgniteCheckedException e) { - skip.add(entry.key()); + e.getValue().updateCacheEntry(); + } catch (IgniteCheckedException ex) { + skipped.add(entry.key()); } } } @@ -235,57 +206,81 @@ private void unlockEntries(Collection locked, AffinityTopolog // At least RuntimeException can be thrown by the code above when GridCacheContext is cleaned and there is // an attempt to use cleaned resources. // That's why releasing locks in the finally block.. - for (CacheMapEntryInfo info : locked) { + for (CacheMapEntryInfo info : infos.values()) { GridCacheMapEntry entry = info.cacheEntry(); + if (entry != null) entry.unlockEntry(); } } // Try evict partitions. - for (CacheMapEntryInfo info : locked) { + for (CacheMapEntryInfo info : infos.values()) { GridDhtCacheEntry entry = info.cacheEntry(); if (entry != null) entry.onUnlock(); } - if (skip != null && skip.size() == size) + if (skipped.size() == size) // Optimization. return; // Must touch all entries since update may have deleted entries. // Eviction manager will remove empty entries. - for (CacheMapEntryInfo info : locked) { + for (CacheMapEntryInfo info : infos.values()) { GridCacheMapEntry entry = info.cacheEntry(); - if (entry != null && (skip == null || !skip.contains(entry.key()))) + + if (entry != null && !skipped.contains(entry.key())) entry.touch(); } } + /** + * @return Count of batch entries. + */ + public int size() { + return infos.size() - skipped.size(); + } + + /** + * @return Off heap update closure. + */ + public OffheapInvokeAllClosure updateClosure() { + return new UpdateAllClosure(this, cctx, part); + } + /** */ - public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvokeAllClosure { + private static class UpdateAllClosure implements OffheapInvokeAllClosure { /** */ private static final long serialVersionUID = -4782459128689696534L; /** */ - private final List> resBatch = new ArrayList<>(entries.size()); + private final List> resBatch; + + /** */ + private final int cacheId; /** */ - private final int cacheId = context().group().storeCacheIdInDataPage() ? context().cacheId() : CU.UNDEFINED_CACHE_ID; + private final CacheMapEntries batch; /** */ - private final int partId = part().id(); + public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtLocalPartition part) { + batch = entries; + resBatch = new ArrayList<>(entries.size()); + cacheId = cctx.group().storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; + } /** {@inheritDoc} */ @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { List newRows = new ArrayList<>(16); + int partId = batch.part().id(); + GridCacheContext cctx = batch.context(); + for (T2 t2 : rows) { CacheDataRow oldRow = t2.get1(); - KeyCacheObject key = t2.get2().key(); - - CacheMapEntryInfo newRowInfo = get(key); + CacheMapEntryInfo newRowInfo = batch.get(key); try { if (newRowInfo.needUpdate(oldRow)) { @@ -295,9 +290,9 @@ public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvo if (val != null) { if (oldRow != null) { - // todo think about batch updates - newRow = context().offheap().dataStore(part()).createRow( - context(), + // todo batch updates + newRow = cctx.offheap().dataStore(batch.part()).createRow( + cctx, key, newRowInfo.value(), newRowInfo.version(), @@ -305,7 +300,7 @@ public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvo oldRow); } else { - CacheObjectContext coCtx = context().cacheObjectContext(); + CacheObjectContext coCtx = cctx.cacheObjectContext(); // todo why we need this val.valueBytes(coCtx); key.valueBytes(coCtx); @@ -324,7 +319,8 @@ public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvo resBatch.add(new T3<>(treeOp, oldRow, newRow)); } else { - // todo we should pass key somehow to remove old row (because in particular case oldRow should not contain key) + // todo we should pass key somehow to remove old row + // todo (in particular case oldRow should not contain key) newRow = new DataRow(key, null, null, 0, 0, 0); resBatch.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); @@ -332,12 +328,12 @@ public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvo } } catch (GridCacheEntryRemovedException e) { - onRemove(key); + batch.onRemove(key); } } if (!newRows.isEmpty()) - context().offheap().dataStore(part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + cctx.offheap().dataStore(batch.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); } /** {@inheritDoc} */ @@ -352,18 +348,15 @@ public class BatchUpdateClosure implements IgniteCacheOffheapManager.OffheapInvo } /** */ - public static class CacheMapEntryInfo { - /** todo think about remove */ - private final BatchedCacheEntries batch; - + private static class CacheMapEntryInfo { /** */ - private final KeyCacheObject key; + private final CacheMapEntries batch; /** */ private final CacheObject val; /** */ - private final long expTime; + private final long expireTime; /** */ private final long ttl; @@ -382,30 +375,21 @@ public static class CacheMapEntryInfo { /** */ public CacheMapEntryInfo( - BatchedCacheEntries batch, - KeyCacheObject key, + CacheMapEntries batch, CacheObject val, - long expTime, + long expireTime, long ttl, GridCacheVersion ver, GridDrType drType ) { this.batch = batch; - this.key = key; this.val = val; - this.expTime = expTime; + this.expireTime = expireTime; this.ver = ver; this.drType = drType; this.ttl = ttl; } - /** - * @return Key. - */ - public KeyCacheObject key() { - return key; - } - /** * @return Version. */ @@ -424,7 +408,7 @@ public CacheObject value() { * @return Expire time. */ public long expireTime() { - return expTime; + return expireTime; } /** @@ -446,7 +430,7 @@ public void updateCacheEntry() throws IgniteCheckedException { if (!update) return; - entry.finishInitialUpdate(val, expTime, ttl, ver, batch.topVer, drType, null, batch.preload); + entry.finishInitialUpdate(val, expireTime, ttl, ver, batch.topVer, drType, null, batch.preload); } /** */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 2b21470a9b0b2..1a34d5b540811 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -41,7 +41,7 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; -import org.apache.ignite.internal.processors.cache.BatchedCacheEntries; +import org.apache.ignite.internal.processors.cache.CacheMapEntries; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; import org.apache.ignite.internal.processors.cache.CacheGroupContext; import org.apache.ignite.internal.processors.cache.CacheMetricsImpl; @@ -96,7 +96,7 @@ public class GridDhtPartitionDemander { /** */ private static final boolean batchPageWriteEnabled = - IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE, false); + IgniteSystemProperties.getBoolean(IgniteSystemProperties.IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE, true); /** */ private final GridCacheSharedContext ctx; @@ -914,7 +914,7 @@ public void preloadEntriesBatch(ClusterNode from, if (entries.isEmpty()) return; - Map cctxMap = new HashMap<>(); + Map cctxMap = new HashMap<>(); // Map by context. for (GridCacheEntryInfo info : entries) { @@ -932,13 +932,13 @@ public void preloadEntriesBatch(ClusterNode from, if (log.isTraceEnabled()) log.trace("Rebalancing key [key=" + info.key() + ", part=" + p + ", node=" + from.id() + ']'); - BatchedCacheEntries batch = cctxMap.get(cctx.cacheId()); + CacheMapEntries batch = cctxMap.get(cctx.cacheId()); if (batch == null) { // todo lock should be called for ALL group cctx.group().listenerLock().readLock().lock(); - cctxMap.put(cctx.cacheId(), batch = new BatchedCacheEntries(topVer, p, cctx, true)); + cctxMap.put(cctx.cacheId(), batch = new CacheMapEntries(topVer, p, cctx, true)); } batch.addEntry(info.key(), info.value(), info.expireTime(), info.ttl(), info.version(), DR_PRELOAD); @@ -949,7 +949,7 @@ public void preloadEntriesBatch(ClusterNode from, } } - for (BatchedCacheEntries batch : cctxMap.values()) { + for (CacheMapEntries batch : cctxMap.values()) { assert batch.size() > BATCH_PRELOAD_THRESHOLD : batch.size(); GridCacheContext cctx = batch.context(); @@ -957,7 +957,7 @@ public void preloadEntriesBatch(ClusterNode from, batch.lock(); try { - cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.new BatchUpdateClosure()); + cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.updateClosure()); } finally { batch.unlock(); @@ -1069,15 +1069,11 @@ private void mvccPreloadEntries(AffinityTopologyVersion topVer, ClusterNode node */ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, int p, Collection infosCol) throws IgniteCheckedException { - GridCacheContext cctx = null; int size = infosCol.size(); - - boolean batchEnabled = - batchPageWriteEnabled && size > BATCH_PRELOAD_THRESHOLD; - - int nBatch = 0; - int total = size / CHECKPOINT_THRESHOLD; + int n = 0; + int cpTail = size % CHECKPOINT_THRESHOLD; + int cpTotal = size <= CHECKPOINT_THRESHOLD ? 1 : size / CHECKPOINT_THRESHOLD; Iterator infos = infosCol.iterator(); @@ -1085,21 +1081,18 @@ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, in while (infos.hasNext()) { ctx.database().checkpointReadLock(); - boolean tail = (nBatch++ >= (total - 1)); - try { - List infosBatch = new ArrayList<>(CHECKPOINT_THRESHOLD); + int cnt = cpTotal == 1 ? size : CHECKPOINT_THRESHOLD + (++n == cpTotal ? cpTail : 0); - for (int i = 0; i < (tail ? CHECKPOINT_THRESHOLD + (size % CHECKPOINT_THRESHOLD) : CHECKPOINT_THRESHOLD); i++) { - if (!infos.hasNext()) - break; + List infosBatch = new ArrayList<>(cnt); + for (int i = 0; i < cnt; i++) { GridCacheEntryInfo entry = infos.next(); infosBatch.add(entry); } - if (batchEnabled && infosBatch.size() > BATCH_PRELOAD_THRESHOLD) + if (batchPageWriteEnabled && infosBatch.size() > BATCH_PRELOAD_THRESHOLD) preloadEntriesBatch(node, p, infosBatch, topVer); else preloadEntriesSingle(node, p, infosBatch, topVer); @@ -1108,46 +1101,6 @@ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, in ctx.database().checkpointReadUnlock(); } } - - // Loop through all received entries and try to preload them. - while (infos.hasNext()) { - ctx.database().checkpointReadLock(); - - try { - for (int i = 0; i < 100; i++) { - if (!infos.hasNext()) - break; - - GridCacheEntryInfo entry = infos.next(); - - if (cctx == null || (grp.sharedGroup() && entry.cacheId() != cctx.cacheId())) { - cctx = grp.sharedGroup() ? grp.shared().cacheContext(entry.cacheId()) : grp.singleCacheContext(); - - if (cctx == null) - continue; - else if (cctx.isNear()) - cctx = cctx.dhtCache().context(); - } - - if (!preloadEntry(node, p, entry, topVer, cctx)) { - if (log.isTraceEnabled()) - log.trace("Got entries for invalid partition during " + - "preloading (will skip) [p=" + p + ", entry=" + entry + ']'); - - return; - } - - //TODO: IGNITE-11330: Update metrics for touched cache only. - for (GridCacheContext ctx : grp.caches()) { - if (ctx.statisticsEnabled()) - ctx.cache().metrics0().onRebalanceKeyReceived(); - } - } - } - finally { - ctx.database().checkpointReadUnlock(); - } - } } /** From 5b608b7140bbcd8244be31bc9655fd43bab38f0a Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 19 Mar 2019 16:04:30 +0300 Subject: [PATCH 04/18] IGNITE-7935 Bug fix. --- .../processors/cache/CacheMapEntries.java | 30 +++++++++++-------- .../cache/persistence/RowStore.java | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index e6c566ca69953..9cfc765440b02 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -272,7 +272,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL /** {@inheritDoc} */ @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { - List newRows = new ArrayList<>(16); + List newRows = new ArrayList<>(16); int partId = batch.part().id(); GridCacheContext cctx = batch.context(); @@ -284,20 +284,20 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL try { if (newRowInfo.needUpdate(oldRow)) { - CacheDataRow newRow; - CacheObject val = newRowInfo.value(); if (val != null) { if (oldRow != null) { // todo batch updates - newRow = cctx.offheap().dataStore(batch.part()).createRow( + CacheDataRow newRow = cctx.offheap().dataStore(batch.part()).createRow( cctx, key, newRowInfo.value(), newRowInfo.version(), newRowInfo.expireTime(), oldRow); + + resBatch.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); } else { CacheObjectContext coCtx = cctx.cacheObjectContext(); @@ -308,20 +308,18 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL if (key.partition() == -1) key.partition(partId); - newRow = new DataRow(key, val, newRowInfo.version(), partId, newRowInfo.expireTime(), cacheId); + DataRow newRow = new DataRow(key, val, newRowInfo.version(), partId, + newRowInfo.expireTime(), cacheId); newRows.add(newRow); - } - IgniteTree.OperationType treeOp = oldRow != null && oldRow.link() == newRow.link() ? - NOOP : PUT; - - resBatch.add(new T3<>(treeOp, oldRow, newRow)); + resBatch.add(new T3<>(PUT, oldRow, newRow)); + } } else { // todo we should pass key somehow to remove old row // todo (in particular case oldRow should not contain key) - newRow = new DataRow(key, null, null, 0, 0, 0); + DataRow newRow = new DataRow(key, null, null, 0, 0, cacheId); resBatch.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); } @@ -332,8 +330,16 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL } } - if (!newRows.isEmpty()) + if (!newRows.isEmpty()) { cctx.offheap().dataStore(batch.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + + if (cacheId == CU.UNDEFINED_CACHE_ID) { + // Set cacheId before write keys into tree. + for (DataRow row : newRows) + row.cacheId(cctx.cacheId()); + + } + } } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java index 2f2942d51ce1d..5b721710bde89 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/RowStore.java @@ -116,7 +116,7 @@ public void addRow(CacheDataRow row, IoStatisticsHolder statHolder) throws Ignit * @param rows Rows. * @throws IgniteCheckedException If failed. */ - public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { + public void addRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { if (!persistenceEnabled) freeList.insertDataRows(rows, statHolder); else { From c8fd56aa70d2a4b04f4346525e301f3aa2ff01c0 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 19 Mar 2019 16:58:24 +0300 Subject: [PATCH 05/18] IGNITE-7935 Cleanup code duplication. --- .../processors/cache/GridCacheMapEntry.java | 72 +------------------ 1 file changed, 3 insertions(+), 69 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java index ced399e0c9c4a..46447f5138217 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java @@ -3349,6 +3349,8 @@ private boolean skipInterceptor(@Nullable GridCacheVersion explicitVer) { }; if (unswapped) { + assert false : key; + update = p.apply(null); if (update) { @@ -3407,75 +3409,7 @@ else if (val == null) } if (update) { - update(val, expTime, ttl, ver, true); - - boolean skipQryNtf = false; - - if (val == null) { - skipQryNtf = true; - - if (cctx.deferredDelete() && !deletedUnlocked() && !isInternal()) - deletedUnlocked(true); - } - else if (deletedUnlocked()) - deletedUnlocked(false); - - long updateCntr = 0; - - if (!preload) - updateCntr = nextPartitionCounter(topVer, true, null); - - if (walEnabled) { - if (cctx.mvccEnabled()) { - cctx.shared().wal().log(new MvccDataRecord(new MvccDataEntry( - cctx.cacheId(), - key, - val, - val == null ? DELETE : GridCacheOperation.CREATE, - null, - ver, - expireTime, - partition(), - updateCntr, - mvccVer == null ? MvccUtils.INITIAL_VERSION : mvccVer - ))); - } else { - cctx.shared().wal().log(new DataRecord(new DataEntry( - cctx.cacheId(), - key, - val, - val == null ? DELETE : GridCacheOperation.CREATE, - null, - ver, - expireTime, - partition(), - updateCntr - ))); - } - } - - drReplicate(drType, val, ver, topVer); - - if (!skipQryNtf) { - cctx.continuousQueries().onEntryUpdated( - key, - val, - null, - this.isInternal() || !this.context().userCache(), - this.partition(), - true, - preload, - updateCntr, - null, - topVer); - } - - onUpdateFinished(updateCntr); - - if (!fromStore && cctx.store().isLocal()) { - if (val != null) - cctx.store().put(null, key, val, ver); - } + finishInitialUpdate(val, expireTime, ttl, ver, topVer, drType, mvccVer, preload); return true; } From 5f6e1767c67f81553a539012a819d08d8f39be04 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 20 Mar 2019 17:40:57 +0300 Subject: [PATCH 06/18] IGNITE-7935 Fixed fake invokeAll implementation. --- .../preloader/GridDhtPartitionDemander.java | 2 +- .../processors/cache/tree/CacheDataTree.java | 56 ++++++++++--------- .../FreeListPreloadWithBatchUpdatesTest.java | 4 +- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 1a34d5b540811..9501a3314d64d 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -41,9 +41,9 @@ import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; import org.apache.ignite.internal.processors.affinity.AffinityAssignment; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; -import org.apache.ignite.internal.processors.cache.CacheMapEntries; import org.apache.ignite.internal.processors.cache.CacheEntryInfoCollection; import org.apache.ignite.internal.processors.cache.CacheGroupContext; +import org.apache.ignite.internal.processors.cache.CacheMapEntries; import org.apache.ignite.internal.processors.cache.CacheMetricsImpl; import org.apache.ignite.internal.processors.cache.GridCacheContext; import org.apache.ignite.internal.processors.cache.GridCacheEntryEx; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java index 7546f0569c3dc..afcf5d791603e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java @@ -357,37 +357,43 @@ public CacheDataRowStore rowStore() { GridCursor cur = find(lower, upper, CacheDataRowAdapter.RowData.FULL); - CacheSearchRow lastSearchRow = null; - KeyCacheObject newKey = null; + CacheSearchRow row = null; + KeyCacheObject key = null; + + CacheDataRow oldRow = null; + KeyCacheObject oldKey = null; while (cur.next()) { - CacheDataRow oldRow = cur.get(); - KeyCacheObject oldKey = oldRow.key(); - - while (newKey == null || newKey.hashCode() <= oldKey.hashCode()) { - if (newKey != null && newKey.hashCode() == oldKey.hashCode()) { - while (newKey.hashCode() == oldKey.hashCode()) { - if (newKey.equals(oldKey)) - batch.add(new T2<>(oldRow, lastSearchRow)); + oldRow = cur.get(); + oldKey = oldRow.key(); + + while (key == null || key.hashCode() <= oldKey.hashCode()) { + if (key != null && key.hashCode() == oldKey.hashCode()) { + while (key.hashCode() == oldKey.hashCode()) { + if (key.equals(oldKey)) + batch.add(new T2<>(oldRow, row)); else - batch.add(new T2<>(null, lastSearchRow)); + batch.add(new T2<>(null, row)); + + row = null; if (!rowItr.hasNext()) break; - lastSearchRow = rowItr.next(); - newKey = lastSearchRow.key(); + row = rowItr.next(); + key = row.key(); } } else { - if (lastSearchRow != null) - batch.add(new T2<>(null, lastSearchRow)); + if (row != null) + batch.add(new T2<>(null, row)); - if (!rowItr.hasNext()) - break; + row = null; - lastSearchRow = rowItr.next(); - newKey = lastSearchRow.key(); + if (rowItr.hasNext()) { + row = rowItr.next(); + key = row.key(); + } } if (!rowItr.hasNext()) @@ -395,6 +401,9 @@ public CacheDataRowStore rowStore() { } } + if (row != null) + batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); + while (rowItr.hasNext()) batch.add(new T2<>(null, rowItr.next())); @@ -402,14 +411,11 @@ public CacheDataRowStore rowStore() { for (T3 t3 : c.result()) { OperationType oper = t3.get1(); - CacheDataRow oldRow = t3.get2(); - CacheDataRow newRow = t3.get3(); if (oper == OperationType.PUT) - put(newRow); - else - if (oper == OperationType.REMOVE) - remove(oldRow); + put(t3.get3()); + else if (oper == OperationType.REMOVE) + remove(t3.get2()); // old row } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java index c4f6c801224b8..61e02bd8c032e 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java @@ -51,7 +51,6 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE; import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_WAL_REBALANCE_THRESHOLD; -import static org.junit.Assert.assertArrayEquals; /** * @@ -233,6 +232,9 @@ public void testBatchHistoricalRebalance() throws Exception { for (int i = 0; i < cnt; i++) { byte[] obj = new byte[ThreadLocalRandom.current().nextInt(16384)]; + if (i > 0 && i < 1000 && i % 37 == 0) + continue; + srcMap.put(i, obj); } From 10b943a10b216ebc2a2b923dec193f769a6a35c7 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 20 Mar 2019 20:05:01 +0300 Subject: [PATCH 07/18] IGNITE-7935 Code cleanup. --- .../ignite/internal/processors/cache/GridCacheMapEntry.java | 4 +--- .../distributed/dht/preloader/GridDhtPartitionDemander.java | 2 -- .../cache/persistence/freelist/AbstractFreeList.java | 6 ++---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java index 46447f5138217..726686cc5232c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheMapEntry.java @@ -3349,8 +3349,6 @@ private boolean skipInterceptor(@Nullable GridCacheVersion explicitVer) { }; if (unswapped) { - assert false : key; - update = p.apply(null); if (update) { @@ -3518,7 +3516,7 @@ else if (deletedUnlocked()) this.isInternal() || !this.context().userCache(), this.partition(), true, - true, + preload, updateCntr, null, topVer); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 9501a3314d64d..675b01248a42c 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -950,8 +950,6 @@ public void preloadEntriesBatch(ClusterNode from, } for (CacheMapEntries batch : cctxMap.values()) { - assert batch.size() > BATCH_PRELOAD_THRESHOLD : batch.size(); - GridCacheContext cctx = batch.context(); batch.lock(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 6578e8e9c0691..9fabdf0439006 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -326,12 +326,10 @@ private class WriteRowsHandler extends WriteRowHandler { AbstractDataPageIO iox = (AbstractDataPageIO)io; - // todo !! DO NOT FORGET WAL DELTA !! - if (iox.getFreeSpace(pageAddr) == maxPayloadSize) { + // todo !! optimize WAL recording + if (iox.getFreeSpace(pageAddr) == maxPayloadSize && !needWalDeltaRecord(pageId, page, null)) { // todo save links for WAL - iox.addRows(pageMem, pageId, pageAddr, args, pageSize()); - // todo update wal } else { From 1394aadff47e532e52cd4d6bcd68fc9e8d38226f Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Fri, 22 Mar 2019 00:23:22 +0300 Subject: [PATCH 08/18] IGNITE-7935 (wip) NPE fix. --- .../processors/cache/tree/CacheDataTree.java | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java index afcf5d791603e..14f20cc08f14b 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java @@ -334,29 +334,23 @@ public CacheDataRowStore rowStore() { /** * todo fake implementation only for checking that closure is working properly with preloader. - * @param keys Keys. - * @param x Implementation specific argument, {@code null} always means that we need a full detached data row. - * @param c Closure. - * @throws IgniteCheckedException If failed. + * todo rework */ @Override public void invokeAll(List keys, Object x, InvokeAllClosure c) throws IgniteCheckedException { checkDestroyed(); - int cnt = keys.size(); - - assert cnt > 0 : cnt; - CacheSearchRow lower = keys.get(0); - CacheSearchRow upper = keys.get(cnt - 1); + CacheSearchRow upper = keys.get(keys.size() - 1); - List> batch = new ArrayList<>(cnt); + List> batch = new ArrayList<>(keys.size()); Iterator rowItr = keys.iterator(); - assert lower.key().hashCode() <= upper.key().hashCode() : "Keys must be lower=" + lower.key().hashCode() + ", upper=" + upper.key().hashCode(); + assert lower.key().hashCode() <= upper.key().hashCode() : "Keys not sorted, lower=" + lower.key().hashCode() + ", upper=" + upper.key().hashCode(); GridCursor cur = find(lower, upper, CacheDataRowAdapter.RowData.FULL); + CacheSearchRow lastRow = null; CacheSearchRow row = null; KeyCacheObject key = null; @@ -370,17 +364,15 @@ public CacheDataRowStore rowStore() { while (key == null || key.hashCode() <= oldKey.hashCode()) { if (key != null && key.hashCode() == oldKey.hashCode()) { while (key.hashCode() == oldKey.hashCode()) { - if (key.equals(oldKey)) - batch.add(new T2<>(oldRow, row)); - else - batch.add(new T2<>(null, row)); + // todo fix and test collision resolution + batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); - row = null; + lastRow = null; if (!rowItr.hasNext()) break; - row = rowItr.next(); + lastRow = row = rowItr.next(); key = row.key(); } } @@ -388,11 +380,11 @@ public CacheDataRowStore rowStore() { if (row != null) batch.add(new T2<>(null, row)); - row = null; + lastRow = null; if (rowItr.hasNext()) { - row = rowItr.next(); - key = row.key(); + lastRow = row = rowItr.next(); + key = lastRow.key(); } } @@ -401,8 +393,8 @@ public CacheDataRowStore rowStore() { } } - if (row != null) - batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); + if (lastRow != null) + batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, lastRow)); while (rowItr.hasNext()) batch.add(new T2<>(null, rowItr.next())); From 4432964067b1e673930a9c0592f646c2188080fe Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Fri, 22 Mar 2019 10:56:21 +0300 Subject: [PATCH 09/18] IGNITE-7935 unsorted keys. --- .../processors/cache/CacheMapEntries.java | 16 +++- .../processors/cache/tree/CacheDataTree.java | 94 ++++++++++--------- .../ignite/internal/util/IgniteTree.java | 7 ++ 3 files changed, 73 insertions(+), 44 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index 9cfc765440b02..d662bdc015cbe 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -67,6 +67,12 @@ public class CacheMapEntries { /** */ private Set skipped = new HashSet<>(); + /** */ + private boolean sorted = true; + + /** */ + private KeyCacheObject temp; + /** */ public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheContext cctx, boolean preload) { this.topVer = topVer; @@ -77,7 +83,10 @@ public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheCont /** */ public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { - infos.put(key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); + if (temp != null && sorted && temp.hashCode() >= key.hashCode()) + sorted = false; + + infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); } /** */ @@ -347,6 +356,11 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL return resBatch; } + /** {@inheritDoc} */ + @Override public boolean fastpath() { + return batch.sorted; + } + /** {@inheritDoc} */ @Override public boolean apply(CacheDataRow row) { return false; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java index 14f20cc08f14b..409e2c881d070 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java @@ -333,72 +333,80 @@ public CacheDataRowStore rowStore() { } /** - * todo fake implementation only for checking that closure is working properly with preloader. + * todo workaround (fake) implementation only for checking that closure is working properly with preloader. * todo rework + * todo remove flag sorted */ - @Override public void invokeAll(List keys, Object x, InvokeAllClosure c) throws IgniteCheckedException { + @Override public void invokeAll( + List keys, + Object x, + InvokeAllClosure c + ) throws IgniteCheckedException { checkDestroyed(); - CacheSearchRow lower = keys.get(0); - CacheSearchRow upper = keys.get(keys.size() - 1); - List> batch = new ArrayList<>(keys.size()); - Iterator rowItr = keys.iterator(); + if (c.fastpath()) { + GridCursor cur = find(keys.get(0), keys.get(keys.size() - 1), CacheDataRowAdapter.RowData.FULL); + Iterator keyItr = keys.iterator(); - assert lower.key().hashCode() <= upper.key().hashCode() : "Keys not sorted, lower=" + lower.key().hashCode() + ", upper=" + upper.key().hashCode(); + CacheSearchRow lastRow = null; + CacheSearchRow row = null; + KeyCacheObject key = null; - GridCursor cur = find(lower, upper, CacheDataRowAdapter.RowData.FULL); + CacheDataRow oldRow = null; + KeyCacheObject oldKey = null; - CacheSearchRow lastRow = null; - CacheSearchRow row = null; - KeyCacheObject key = null; + while (cur.next()) { + oldRow = cur.get(); + oldKey = oldRow.key(); - CacheDataRow oldRow = null; - KeyCacheObject oldKey = null; + while (key == null || key.hashCode() <= oldKey.hashCode()) { + if (key != null && key.hashCode() == oldKey.hashCode()) { + while (key.hashCode() == oldKey.hashCode()) { + // todo fix and test collision resolution + batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); - while (cur.next()) { - oldRow = cur.get(); - oldKey = oldRow.key(); + lastRow = null; - while (key == null || key.hashCode() <= oldKey.hashCode()) { - if (key != null && key.hashCode() == oldKey.hashCode()) { - while (key.hashCode() == oldKey.hashCode()) { - // todo fix and test collision resolution - batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); + if (!keyItr.hasNext()) + break; - lastRow = null; + lastRow = row = keyItr.next(); + key = row.key(); + } + } + else { + if (row != null) + batch.add(new T2<>(null, row)); - if (!rowItr.hasNext()) - break; + lastRow = null; - lastRow = row = rowItr.next(); - key = row.key(); + if (keyItr.hasNext()) { + lastRow = row = keyItr.next(); + key = lastRow.key(); + } } + + if (!keyItr.hasNext()) + break; } - else { - if (row != null) - batch.add(new T2<>(null, row)); + } - lastRow = null; + if (lastRow != null) + batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, lastRow)); - if (rowItr.hasNext()) { - lastRow = row = rowItr.next(); - key = lastRow.key(); - } - } + while (keyItr.hasNext()) + batch.add(new T2<>(null, keyItr.next())); + } else { + for (CacheSearchRow row : keys) { + // todo NO_KEY + CacheDataRow oldRow = findOne(row, null, CacheDataRowAdapter.RowData.FULL); - if (!rowItr.hasNext()) - break; + batch.add(new T2<>(oldRow, row)); } } - if (lastRow != null) - batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, lastRow)); - - while (rowItr.hasNext()) - batch.add(new T2<>(null, rowItr.next())); - c.call(batch); for (T3 t3 : c.result()) { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java index 12d1a6d3918dc..0d39feee654dd 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java @@ -159,6 +159,13 @@ interface InvokeAllClosure { * @return operation, old row, new row */ Collection> result(); + + /** + * @return Fast path flag. + * @deprecated Workaround to select B+ tree search strategy, should be removed in final implementation. + */ + @Deprecated + boolean fastpath(); } /** From aa058e67407539dd2a0862316db9b2633c238951 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Fri, 22 Mar 2019 17:15:09 +0300 Subject: [PATCH 10/18] IGNITE-7935 Workaround for eviction policies. --- .../dht/preloader/GridDhtPartitionDemander.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 675b01248a42c..98e7ead7adb58 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -34,6 +34,7 @@ import org.apache.ignite.cache.CacheRebalanceMode; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.configuration.CacheConfiguration; +import org.apache.ignite.configuration.DataPageEvictionMode; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.events.DiscoveryEvent; import org.apache.ignite.internal.IgniteInternalFuture; @@ -1075,6 +1076,10 @@ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, in Iterator infos = infosCol.iterator(); + // todo + boolean batchPageWriteEnabled0 = batchPageWriteEnabled && (grp.dataRegion().config().isPersistenceEnabled() || + grp.dataRegion().config().getPageEvictionMode() == DataPageEvictionMode.DISABLED); + // Loop through all received entries and try to preload them. while (infos.hasNext()) { ctx.database().checkpointReadLock(); @@ -1090,7 +1095,7 @@ private void preloadEntries(AffinityTopologyVersion topVer, ClusterNode node, in infosBatch.add(entry); } - if (batchPageWriteEnabled && infosBatch.size() > BATCH_PRELOAD_THRESHOLD) + if (batchPageWriteEnabled0 && infosBatch.size() > BATCH_PRELOAD_THRESHOLD) preloadEntriesBatch(node, p, infosBatch, topVer); else preloadEntriesSingle(node, p, infosBatch, topVer); From 8d679b715ffab9b7c2f8f781488ab171f4663384 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Sat, 23 Mar 2019 19:07:26 +0300 Subject: [PATCH 11/18] IGNITE-7935 Removed search by key. --- .../jmh/pagemem/JmhBatchUpdatesBenchmark.java | 2 +- .../processors/cache/CacheMapEntries.java | 139 ++++++++++-------- 2 files changed, 80 insertions(+), 61 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java index a6e565f337132..ff5c452ccb0a7 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java @@ -131,7 +131,7 @@ public enum RANGE { r4000_16000(4_000, 16_000), /** Mixed objects, mostly large objects. */ - r0_32000(100, 32_000); + r100_32000(100, 32_000); /** */ private final int min; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index d662bdc015cbe..a7ffad6c8a9f6 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -20,9 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; @@ -35,6 +33,7 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.dr.GridDrType; import org.apache.ignite.internal.util.IgniteTree; +import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.CU; @@ -62,7 +61,7 @@ public class CacheMapEntries { private final boolean preload; /** */ - private final LinkedHashMap infos = new LinkedHashMap<>(); + private final List infos = new ArrayList<>(8); /** */ private Set skipped = new HashSet<>(); @@ -86,17 +85,12 @@ public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl if (temp != null && sorted && temp.hashCode() >= key.hashCode()) sorted = false; - infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); + infos.add(new CacheMapEntryInfo(this, temp = key, val, expTime, ttl, ver, drType)); } /** */ - public Set keys() { - return infos.keySet(); - } - - /** */ - public Collection values() { - return infos.values(); + public boolean preload() { + return preload; } /** */ @@ -110,13 +104,32 @@ public GridCacheContext context() { } /** */ - public CacheMapEntryInfo get(KeyCacheObject key) { - return infos.get(key); + public Collection keys() { + return F.viewReadOnly(infos, CacheMapEntryInfo::key); } /** */ - public boolean preload() { - return preload; + public Collection values() { + return infos; + } + + /** */ + public CacheMapEntryInfo get(int idx) { + return infos.get(idx); + } + + /** + * @return Count of batch entries. + */ + public int size() { + return infos.size() - skipped.size(); + } + + /** + * @return Off heap update closure. + */ + public OffheapInvokeAllClosure updateClosure() { + return new UpdateAllClosure(this, cctx); } /** */ @@ -139,12 +152,12 @@ public void lock() { List locked = new ArrayList<>(infos.size()); while (true) { - for (Map.Entry e : infos.entrySet()) { - GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(e.getKey(), topVer); + for (CacheMapEntryInfo info : infos) { + GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(info.key(), topVer); locked.add(entry); - e.getValue().cacheEntry(entry); + info.cacheEntry(entry); } boolean retry = false; @@ -187,6 +200,7 @@ public void lock() { */ public void unlock() { // Process deleted entries before locks release. + // todo assert cctx.deferredDelete() : this; // Entries to skip eviction manager notification for. @@ -194,9 +208,9 @@ public void unlock() { int size = infos.size(); try { - for (Map.Entry e : infos.entrySet()) { - KeyCacheObject key = e.getKey(); - GridCacheMapEntry entry = e.getValue().cacheEntry(); + for (CacheMapEntryInfo info : infos) { + KeyCacheObject key = info.key(); + GridCacheMapEntry entry = info.cacheEntry(); if (skipped.contains(key)) continue; @@ -205,7 +219,7 @@ public void unlock() { skipped.add(entry.key()); try { - e.getValue().updateCacheEntry(); + info.updateCacheEntry(); } catch (IgniteCheckedException ex) { skipped.add(entry.key()); } @@ -215,7 +229,7 @@ public void unlock() { // At least RuntimeException can be thrown by the code above when GridCacheContext is cleaned and there is // an attempt to use cleaned resources. // That's why releasing locks in the finally block.. - for (CacheMapEntryInfo info : infos.values()) { + for (CacheMapEntryInfo info : values()) { GridCacheMapEntry entry = info.cacheEntry(); if (entry != null) @@ -224,7 +238,7 @@ public void unlock() { } // Try evict partitions. - for (CacheMapEntryInfo info : infos.values()) { + for (CacheMapEntryInfo info : values()) { GridDhtCacheEntry entry = info.cacheEntry(); if (entry != null) entry.onUnlock(); @@ -236,7 +250,7 @@ public void unlock() { // Must touch all entries since update may have deleted entries. // Eviction manager will remove empty entries. - for (CacheMapEntryInfo info : infos.values()) { + for (CacheMapEntryInfo info : values()) { GridCacheMapEntry entry = info.cacheEntry(); if (entry != null && !skipped.contains(entry.key())) @@ -244,52 +258,43 @@ public void unlock() { } } - /** - * @return Count of batch entries. - */ - public int size() { - return infos.size() - skipped.size(); - } - - /** - * @return Off heap update closure. - */ - public OffheapInvokeAllClosure updateClosure() { - return new UpdateAllClosure(this, cctx, part); - } - /** */ private static class UpdateAllClosure implements OffheapInvokeAllClosure { /** */ private static final long serialVersionUID = -4782459128689696534L; /** */ - private final List> resBatch; + private final List> finalOps; /** */ private final int cacheId; /** */ - private final CacheMapEntries batch; + private final CacheMapEntries entries; /** */ - public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtLocalPartition part) { - batch = entries; - resBatch = new ArrayList<>(entries.size()); + public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { + this.entries = entries; + finalOps = new ArrayList<>(entries.size()); cacheId = cctx.group().storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; } /** {@inheritDoc} */ @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { - List newRows = new ArrayList<>(16); + List newRows = new ArrayList<>(8); - int partId = batch.part().id(); - GridCacheContext cctx = batch.context(); + int partId = entries.part().id(); + GridCacheContext cctx = entries.context(); + + assert rows.size() == entries.size() : "size mismatch, expected=" + entries.size() + ", input=" + rows.size(); + + int idx = 0; for (T2 t2 : rows) { CacheDataRow oldRow = t2.get1(); KeyCacheObject key = t2.get2().key(); - CacheMapEntryInfo newRowInfo = batch.get(key); + + CacheMapEntryInfo newRowInfo = entries.get(idx++); try { if (newRowInfo.needUpdate(oldRow)) { @@ -298,7 +303,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL if (val != null) { if (oldRow != null) { // todo batch updates - CacheDataRow newRow = cctx.offheap().dataStore(batch.part()).createRow( + CacheDataRow newRow = cctx.offheap().dataStore(entries.part()).createRow( cctx, key, newRowInfo.value(), @@ -306,7 +311,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL newRowInfo.expireTime(), oldRow); - resBatch.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); + finalOps.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); } else { CacheObjectContext coCtx = cctx.cacheObjectContext(); @@ -322,7 +327,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL newRows.add(newRow); - resBatch.add(new T3<>(PUT, oldRow, newRow)); + finalOps.add(new T3<>(PUT, oldRow, newRow)); } } else { @@ -330,20 +335,22 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL // todo (in particular case oldRow should not contain key) DataRow newRow = new DataRow(key, null, null, 0, 0, cacheId); - resBatch.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); + finalOps.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); } } } catch (GridCacheEntryRemovedException e) { - batch.onRemove(key); + entries.onRemove(key); } } if (!newRows.isEmpty()) { - cctx.offheap().dataStore(batch.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + //cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); + + cctx.offheap().dataStore(entries.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); if (cacheId == CU.UNDEFINED_CACHE_ID) { - // Set cacheId before write keys into tree. + // Set cacheId before put keys into tree. for (DataRow row : newRows) row.cacheId(cctx.cacheId()); @@ -353,12 +360,12 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL /** {@inheritDoc} */ @Override public Collection> result() { - return resBatch; + return finalOps; } /** {@inheritDoc} */ @Override public boolean fastpath() { - return batch.sorted; + return entries.sorted; } /** {@inheritDoc} */ @@ -372,6 +379,9 @@ private static class CacheMapEntryInfo { /** */ private final CacheMapEntries batch; + /** */ + private KeyCacheObject key; + /** */ private final CacheObject val; @@ -396,6 +406,7 @@ private static class CacheMapEntryInfo { /** */ public CacheMapEntryInfo( CacheMapEntries batch, + KeyCacheObject key, CacheObject val, long expireTime, long ttl, @@ -403,6 +414,7 @@ public CacheMapEntryInfo( GridDrType drType ) { this.batch = batch; + this.key = key; this.val = val; this.expireTime = expireTime; this.ver = ver; @@ -411,10 +423,10 @@ public CacheMapEntryInfo( } /** - * @return Version. + * @return Key. */ - public GridCacheVersion version() { - return ver; + public KeyCacheObject key() { + return key; } /** @@ -424,6 +436,13 @@ public CacheObject value() { return val; } + /** + * @return Version. + */ + public GridCacheVersion version() { + return ver; + } + /** * @return Expire time. */ From 1a26c887954e418c6c5d43574cdbd330b1eacaaa Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 25 Mar 2019 10:51:52 +0300 Subject: [PATCH 12/18] Revert "IGNITE-7935 Removed search by key." This reverts commit ed498f0e --- .../jmh/pagemem/JmhBatchUpdatesBenchmark.java | 2 +- .../processors/cache/CacheMapEntries.java | 139 ++++++++---------- 2 files changed, 61 insertions(+), 80 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java index ff5c452ccb0a7..a6e565f337132 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java @@ -131,7 +131,7 @@ public enum RANGE { r4000_16000(4_000, 16_000), /** Mixed objects, mostly large objects. */ - r100_32000(100, 32_000); + r0_32000(100, 32_000); /** */ private final int min; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index a7ffad6c8a9f6..d662bdc015cbe 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -20,7 +20,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Set; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; @@ -33,7 +35,6 @@ import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.dr.GridDrType; import org.apache.ignite.internal.util.IgniteTree; -import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.CU; @@ -61,7 +62,7 @@ public class CacheMapEntries { private final boolean preload; /** */ - private final List infos = new ArrayList<>(8); + private final LinkedHashMap infos = new LinkedHashMap<>(); /** */ private Set skipped = new HashSet<>(); @@ -85,12 +86,17 @@ public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl if (temp != null && sorted && temp.hashCode() >= key.hashCode()) sorted = false; - infos.add(new CacheMapEntryInfo(this, temp = key, val, expTime, ttl, ver, drType)); + infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); } /** */ - public boolean preload() { - return preload; + public Set keys() { + return infos.keySet(); + } + + /** */ + public Collection values() { + return infos.values(); } /** */ @@ -104,32 +110,13 @@ public GridCacheContext context() { } /** */ - public Collection keys() { - return F.viewReadOnly(infos, CacheMapEntryInfo::key); + public CacheMapEntryInfo get(KeyCacheObject key) { + return infos.get(key); } /** */ - public Collection values() { - return infos; - } - - /** */ - public CacheMapEntryInfo get(int idx) { - return infos.get(idx); - } - - /** - * @return Count of batch entries. - */ - public int size() { - return infos.size() - skipped.size(); - } - - /** - * @return Off heap update closure. - */ - public OffheapInvokeAllClosure updateClosure() { - return new UpdateAllClosure(this, cctx); + public boolean preload() { + return preload; } /** */ @@ -152,12 +139,12 @@ public void lock() { List locked = new ArrayList<>(infos.size()); while (true) { - for (CacheMapEntryInfo info : infos) { - GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(info.key(), topVer); + for (Map.Entry e : infos.entrySet()) { + GridDhtCacheEntry entry = (GridDhtCacheEntry)cctx.cache().entryEx(e.getKey(), topVer); locked.add(entry); - info.cacheEntry(entry); + e.getValue().cacheEntry(entry); } boolean retry = false; @@ -200,7 +187,6 @@ public void lock() { */ public void unlock() { // Process deleted entries before locks release. - // todo assert cctx.deferredDelete() : this; // Entries to skip eviction manager notification for. @@ -208,9 +194,9 @@ public void unlock() { int size = infos.size(); try { - for (CacheMapEntryInfo info : infos) { - KeyCacheObject key = info.key(); - GridCacheMapEntry entry = info.cacheEntry(); + for (Map.Entry e : infos.entrySet()) { + KeyCacheObject key = e.getKey(); + GridCacheMapEntry entry = e.getValue().cacheEntry(); if (skipped.contains(key)) continue; @@ -219,7 +205,7 @@ public void unlock() { skipped.add(entry.key()); try { - info.updateCacheEntry(); + e.getValue().updateCacheEntry(); } catch (IgniteCheckedException ex) { skipped.add(entry.key()); } @@ -229,7 +215,7 @@ public void unlock() { // At least RuntimeException can be thrown by the code above when GridCacheContext is cleaned and there is // an attempt to use cleaned resources. // That's why releasing locks in the finally block.. - for (CacheMapEntryInfo info : values()) { + for (CacheMapEntryInfo info : infos.values()) { GridCacheMapEntry entry = info.cacheEntry(); if (entry != null) @@ -238,7 +224,7 @@ public void unlock() { } // Try evict partitions. - for (CacheMapEntryInfo info : values()) { + for (CacheMapEntryInfo info : infos.values()) { GridDhtCacheEntry entry = info.cacheEntry(); if (entry != null) entry.onUnlock(); @@ -250,7 +236,7 @@ public void unlock() { // Must touch all entries since update may have deleted entries. // Eviction manager will remove empty entries. - for (CacheMapEntryInfo info : values()) { + for (CacheMapEntryInfo info : infos.values()) { GridCacheMapEntry entry = info.cacheEntry(); if (entry != null && !skipped.contains(entry.key())) @@ -258,43 +244,52 @@ public void unlock() { } } + /** + * @return Count of batch entries. + */ + public int size() { + return infos.size() - skipped.size(); + } + + /** + * @return Off heap update closure. + */ + public OffheapInvokeAllClosure updateClosure() { + return new UpdateAllClosure(this, cctx, part); + } + /** */ private static class UpdateAllClosure implements OffheapInvokeAllClosure { /** */ private static final long serialVersionUID = -4782459128689696534L; /** */ - private final List> finalOps; + private final List> resBatch; /** */ private final int cacheId; /** */ - private final CacheMapEntries entries; + private final CacheMapEntries batch; /** */ - public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { - this.entries = entries; - finalOps = new ArrayList<>(entries.size()); + public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtLocalPartition part) { + batch = entries; + resBatch = new ArrayList<>(entries.size()); cacheId = cctx.group().storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; } /** {@inheritDoc} */ @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { - List newRows = new ArrayList<>(8); + List newRows = new ArrayList<>(16); - int partId = entries.part().id(); - GridCacheContext cctx = entries.context(); - - assert rows.size() == entries.size() : "size mismatch, expected=" + entries.size() + ", input=" + rows.size(); - - int idx = 0; + int partId = batch.part().id(); + GridCacheContext cctx = batch.context(); for (T2 t2 : rows) { CacheDataRow oldRow = t2.get1(); KeyCacheObject key = t2.get2().key(); - - CacheMapEntryInfo newRowInfo = entries.get(idx++); + CacheMapEntryInfo newRowInfo = batch.get(key); try { if (newRowInfo.needUpdate(oldRow)) { @@ -303,7 +298,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { if (val != null) { if (oldRow != null) { // todo batch updates - CacheDataRow newRow = cctx.offheap().dataStore(entries.part()).createRow( + CacheDataRow newRow = cctx.offheap().dataStore(batch.part()).createRow( cctx, key, newRowInfo.value(), @@ -311,7 +306,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { newRowInfo.expireTime(), oldRow); - finalOps.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); + resBatch.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); } else { CacheObjectContext coCtx = cctx.cacheObjectContext(); @@ -327,7 +322,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { newRows.add(newRow); - finalOps.add(new T3<>(PUT, oldRow, newRow)); + resBatch.add(new T3<>(PUT, oldRow, newRow)); } } else { @@ -335,22 +330,20 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { // todo (in particular case oldRow should not contain key) DataRow newRow = new DataRow(key, null, null, 0, 0, cacheId); - finalOps.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); + resBatch.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); } } } catch (GridCacheEntryRemovedException e) { - entries.onRemove(key); + batch.onRemove(key); } } if (!newRows.isEmpty()) { - //cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); - - cctx.offheap().dataStore(entries.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + cctx.offheap().dataStore(batch.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); if (cacheId == CU.UNDEFINED_CACHE_ID) { - // Set cacheId before put keys into tree. + // Set cacheId before write keys into tree. for (DataRow row : newRows) row.cacheId(cctx.cacheId()); @@ -360,12 +353,12 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { /** {@inheritDoc} */ @Override public Collection> result() { - return finalOps; + return resBatch; } /** {@inheritDoc} */ @Override public boolean fastpath() { - return entries.sorted; + return batch.sorted; } /** {@inheritDoc} */ @@ -379,9 +372,6 @@ private static class CacheMapEntryInfo { /** */ private final CacheMapEntries batch; - /** */ - private KeyCacheObject key; - /** */ private final CacheObject val; @@ -406,7 +396,6 @@ private static class CacheMapEntryInfo { /** */ public CacheMapEntryInfo( CacheMapEntries batch, - KeyCacheObject key, CacheObject val, long expireTime, long ttl, @@ -414,7 +403,6 @@ public CacheMapEntryInfo( GridDrType drType ) { this.batch = batch; - this.key = key; this.val = val; this.expireTime = expireTime; this.ver = ver; @@ -423,10 +411,10 @@ public CacheMapEntryInfo( } /** - * @return Key. + * @return Version. */ - public KeyCacheObject key() { - return key; + public GridCacheVersion version() { + return ver; } /** @@ -436,13 +424,6 @@ public CacheObject value() { return val; } - /** - * @return Version. - */ - public GridCacheVersion version() { - return ver; - } - /** * @return Expire time. */ From 745580ae0a5f1ee1c4c29ca3e1a2bd7ebe4f567f Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 25 Mar 2019 11:13:15 +0300 Subject: [PATCH 13/18] IGNITE-7935 code cleanup (wip). --- .../jmh/pagemem/JmhBatchUpdatesBenchmark.java | 2 +- .../processors/cache/CacheMapEntries.java | 122 +++++++++--------- .../preloader/GridDhtPartitionDemander.java | 2 +- .../processors/cache/tree/CacheDataTree.java | 14 +- 4 files changed, 73 insertions(+), 67 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java index a6e565f337132..ff5c452ccb0a7 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java @@ -131,7 +131,7 @@ public enum RANGE { r4000_16000(4_000, 16_000), /** Mixed objects, mostly large objects. */ - r0_32000(100, 32_000); + r100_32000(100, 32_000); /** */ private final int min; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index d662bdc015cbe..885da03cc1e44 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -68,7 +68,7 @@ public class CacheMapEntries { private Set skipped = new HashSet<>(); /** */ - private boolean sorted = true; + private boolean ordered = true; /** */ private KeyCacheObject temp; @@ -83,20 +83,13 @@ public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheCont /** */ public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { - if (temp != null && sorted && temp.hashCode() >= key.hashCode()) - sorted = false; + if (temp != null && ordered && temp.hashCode() >= key.hashCode()) + ordered = false; - infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); - } + CacheMapEntryInfo old = infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); - /** */ - public Set keys() { - return infos.keySet(); - } - - /** */ - public Collection values() { - return infos.values(); + assert old == null || ATOMIC_VER_COMPARATOR.compare(old.version(), ver) < 0 : + "Entry version mismatch: prev=" + old.version() + ", current=" + ver; } /** */ @@ -110,28 +103,22 @@ public GridCacheContext context() { } /** */ - public CacheMapEntryInfo get(KeyCacheObject key) { - return infos.get(key); - } - - /** */ - public boolean preload() { - return preload; - } - - /** */ - public void onRemove(KeyCacheObject key) { - skipped.add(key); + public Set keys() { + return infos.keySet(); } - /** */ - public void onError(KeyCacheObject key, IgniteCheckedException e) { - skipped.add(key); + /** + * @return Count of batch entries. + */ + public int size() { + return infos.size() - skipped.size(); } - /** */ - public boolean skip(KeyCacheObject key) { - return skipped.contains(key); + /** + * @return Off heap update closure. + */ + public OffheapInvokeAllClosure offheapUpdateClosure() { + return new UpdateAllClosure(this, cctx); } /** */ @@ -187,6 +174,7 @@ public void lock() { */ public void unlock() { // Process deleted entries before locks release. + // todo assert cctx.deferredDelete() : this; // Entries to skip eviction manager notification for. @@ -244,18 +232,34 @@ public void unlock() { } } - /** - * @return Count of batch entries. - */ - public int size() { - return infos.size() - skipped.size(); + /** */ + public void onRemove(KeyCacheObject key) { + skipped.add(key); } - /** - * @return Off heap update closure. - */ - public OffheapInvokeAllClosure updateClosure() { - return new UpdateAllClosure(this, cctx, part); + /** */ + public void onError(KeyCacheObject key, IgniteCheckedException e) { + skipped.add(key); + } + + /** */ + boolean preload() { + return preload; + } + + /** */ + Collection values() { + return infos.values(); + } + + /** */ + CacheMapEntryInfo get(KeyCacheObject key) { + return infos.get(key); + } + + /** */ + boolean skip(KeyCacheObject key) { + return skipped.contains(key); } /** */ @@ -264,32 +268,34 @@ private static class UpdateAllClosure implements OffheapInvokeAllClosure { private static final long serialVersionUID = -4782459128689696534L; /** */ - private final List> resBatch; + private final List> finalOps; /** */ private final int cacheId; /** */ - private final CacheMapEntries batch; + private final CacheMapEntries entries; /** */ - public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtLocalPartition part) { - batch = entries; - resBatch = new ArrayList<>(entries.size()); + public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { + this.entries = entries; + finalOps = new ArrayList<>(entries.size()); cacheId = cctx.group().storeCacheIdInDataPage() ? cctx.cacheId() : CU.UNDEFINED_CACHE_ID; } /** {@inheritDoc} */ @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { - List newRows = new ArrayList<>(16); + List newRows = new ArrayList<>(8); - int partId = batch.part().id(); - GridCacheContext cctx = batch.context(); + int partId = entries.part().id(); + GridCacheContext cctx = entries.context(); + + assert rows.size() == entries.size() : "size mismatch, expected=" + entries.size() + ", input=" + rows.size(); for (T2 t2 : rows) { CacheDataRow oldRow = t2.get1(); KeyCacheObject key = t2.get2().key(); - CacheMapEntryInfo newRowInfo = batch.get(key); + CacheMapEntryInfo newRowInfo = entries.get(key); try { if (newRowInfo.needUpdate(oldRow)) { @@ -298,7 +304,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL if (val != null) { if (oldRow != null) { // todo batch updates - CacheDataRow newRow = cctx.offheap().dataStore(batch.part()).createRow( + CacheDataRow newRow = cctx.offheap().dataStore(entries.part()).createRow( cctx, key, newRowInfo.value(), @@ -306,7 +312,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL newRowInfo.expireTime(), oldRow); - resBatch.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); + finalOps.add(new T3<>(oldRow.link() == newRow.link() ? NOOP : PUT, oldRow, newRow)); } else { CacheObjectContext coCtx = cctx.cacheObjectContext(); @@ -322,7 +328,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL newRows.add(newRow); - resBatch.add(new T3<>(PUT, oldRow, newRow)); + finalOps.add(new T3<>(PUT, oldRow, newRow)); } } else { @@ -330,17 +336,17 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL // todo (in particular case oldRow should not contain key) DataRow newRow = new DataRow(key, null, null, 0, 0, cacheId); - resBatch.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); + finalOps.add(new T3<>(oldRow != null ? REMOVE : NOOP, oldRow, newRow)); } } } catch (GridCacheEntryRemovedException e) { - batch.onRemove(key); + entries.onRemove(key); } } if (!newRows.isEmpty()) { - cctx.offheap().dataStore(batch.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + cctx.offheap().dataStore(entries.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); if (cacheId == CU.UNDEFINED_CACHE_ID) { // Set cacheId before write keys into tree. @@ -353,12 +359,12 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx, GridDhtL /** {@inheritDoc} */ @Override public Collection> result() { - return resBatch; + return finalOps; } /** {@inheritDoc} */ @Override public boolean fastpath() { - return batch.sorted; + return entries.ordered; } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 98e7ead7adb58..3dea8074570df 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -956,7 +956,7 @@ public void preloadEntriesBatch(ClusterNode from, batch.lock(); try { - cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.updateClosure()); + cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.offheapUpdateClosure()); } finally { batch.unlock(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java index 409e2c881d070..f2c66df01f2e1 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java @@ -344,7 +344,7 @@ public CacheDataRowStore rowStore() { ) throws IgniteCheckedException { checkDestroyed(); - List> batch = new ArrayList<>(keys.size()); + List> rows = new ArrayList<>(keys.size()); if (c.fastpath()) { GridCursor cur = find(keys.get(0), keys.get(keys.size() - 1), CacheDataRowAdapter.RowData.FULL); @@ -365,7 +365,7 @@ public CacheDataRowStore rowStore() { if (key != null && key.hashCode() == oldKey.hashCode()) { while (key.hashCode() == oldKey.hashCode()) { // todo fix and test collision resolution - batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); + rows.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); lastRow = null; @@ -378,7 +378,7 @@ public CacheDataRowStore rowStore() { } else { if (row != null) - batch.add(new T2<>(null, row)); + rows.add(new T2<>(null, row)); lastRow = null; @@ -394,20 +394,20 @@ public CacheDataRowStore rowStore() { } if (lastRow != null) - batch.add(new T2<>(key.equals(oldKey) ? oldRow : null, lastRow)); + rows.add(new T2<>(key.equals(oldKey) ? oldRow : null, lastRow)); while (keyItr.hasNext()) - batch.add(new T2<>(null, keyItr.next())); + rows.add(new T2<>(null, keyItr.next())); } else { for (CacheSearchRow row : keys) { // todo NO_KEY CacheDataRow oldRow = findOne(row, null, CacheDataRowAdapter.RowData.FULL); - batch.add(new T2<>(oldRow, row)); + rows.add(new T2<>(oldRow, row)); } } - c.call(batch); + c.call(rows); for (T3 t3 : c.result()) { OperationType oper = t3.get1(); From f5f43cd0ab22ef8d6bb61bc2e8a98fef9d544840 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Mon, 25 Mar 2019 16:45:50 +0300 Subject: [PATCH 14/18] IGNITE-7935 Removed search by key (2). --- .../jmh/pagemem/JmhBatchUpdatesBenchmark.java | 8 +-- .../processors/cache/CacheMapEntries.java | 48 +++++-------- .../cache/IgniteCacheOffheapManager.java | 2 +- .../preloader/GridDhtPartitionDemander.java | 72 +++++++++---------- .../cache/persistence/tree/BPlusTree.java | 2 +- .../processors/cache/tree/CacheDataTree.java | 25 +++---- .../ignite/internal/util/IgniteTree.java | 14 ++-- 7 files changed, 72 insertions(+), 99 deletions(-) diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java index ff5c452ccb0a7..225e702bfea74 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/pagemem/JmhBatchUpdatesBenchmark.java @@ -99,7 +99,7 @@ public class JmhBatchUpdatesBenchmark { private static int iteration = 0; /** */ - public enum RANGE { + public enum OBJECT_SIZE_RANGE { /** */ r0_4(0, 4), @@ -140,13 +140,12 @@ public enum RANGE { private final int max; /** */ - RANGE(int min, int max) { + OBJECT_SIZE_RANGE(int min, int max) { this.min = min; this.max = max; } } - /** * Create Ignite configuration. * @@ -213,7 +212,6 @@ public void checkBatch(Data data, Preloader preloader) throws IgniteCheckedExcep preloader.demanderBatch.preloadEntriesBatch(null, 0, data.batchData, data.cctxBatch.topology().readyTopologyVersion()); } - /** * Start 2 servers and 1 client. */ @@ -311,7 +309,7 @@ private static Object findField(Class cls, Object obj, public static class Data { /** */ @Param - private RANGE range; + private OBJECT_SIZE_RANGE range; /** */ private int[] sizes; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index 885da03cc1e44..132758954a16a 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -30,17 +31,13 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtCacheEntry; import org.apache.ignite.internal.processors.cache.distributed.dht.topology.GridDhtLocalPartition; import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow; -import org.apache.ignite.internal.processors.cache.persistence.CacheSearchRow; import org.apache.ignite.internal.processors.cache.tree.DataRow; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.internal.processors.dr.GridDrType; import org.apache.ignite.internal.util.IgniteTree; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.CU; -import org.jetbrains.annotations.Nullable; -import static org.apache.ignite.internal.processors.cache.GridCacheMapEntry.ATOMIC_VER_COMPARATOR; import static org.apache.ignite.internal.util.IgniteTree.OperationType.NOOP; import static org.apache.ignite.internal.util.IgniteTree.OperationType.PUT; import static org.apache.ignite.internal.util.IgniteTree.OperationType.REMOVE; @@ -82,13 +79,13 @@ public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheCont } /** */ - public void addEntry(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { + public void add(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { if (temp != null && ordered && temp.hashCode() >= key.hashCode()) ordered = false; CacheMapEntryInfo old = infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); - assert old == null || ATOMIC_VER_COMPARATOR.compare(old.version(), ver) < 0 : + assert old == null || GridCacheMapEntry.ATOMIC_VER_COMPARATOR.compare(old.version(), ver) < 0 : "Entry version mismatch: prev=" + old.version() + ", current=" + ver; } @@ -237,26 +234,11 @@ public void onRemove(KeyCacheObject key) { skipped.add(key); } - /** */ - public void onError(KeyCacheObject key, IgniteCheckedException e) { - skipped.add(key); - } - /** */ boolean preload() { return preload; } - /** */ - Collection values() { - return infos.values(); - } - - /** */ - CacheMapEntryInfo get(KeyCacheObject key) { - return infos.get(key); - } - /** */ boolean skip(KeyCacheObject key) { return skipped.contains(key); @@ -284,18 +266,20 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { } /** {@inheritDoc} */ - @Override public void call(@Nullable Collection> rows) throws IgniteCheckedException { + @Override public void call(Collection rows) throws IgniteCheckedException { + assert rows.size() == entries.size() : "size mismatch, expect=" + entries.size() + ", input=" + rows.size(); + List newRows = new ArrayList<>(8); int partId = entries.part().id(); GridCacheContext cctx = entries.context(); - assert rows.size() == entries.size() : "size mismatch, expected=" + entries.size() + ", input=" + rows.size(); + Iterator rowsIter = rows.iterator(); - for (T2 t2 : rows) { - CacheDataRow oldRow = t2.get1(); - KeyCacheObject key = t2.get2().key(); - CacheMapEntryInfo newRowInfo = entries.get(key); + for (Map.Entry e : entries.infos.entrySet()) { + KeyCacheObject key = e.getKey(); + CacheMapEntryInfo newRowInfo = e.getValue(); + CacheDataRow oldRow = rowsIter.next(); try { if (newRowInfo.needUpdate(oldRow)) { @@ -303,7 +287,6 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { if (val != null) { if (oldRow != null) { - // todo batch updates CacheDataRow newRow = cctx.offheap().dataStore(entries.part()).createRow( cctx, key, @@ -316,7 +299,7 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { } else { CacheObjectContext coCtx = cctx.cacheObjectContext(); - // todo why we need this + val.valueBytes(coCtx); key.valueBytes(coCtx); @@ -340,13 +323,14 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { } } } - catch (GridCacheEntryRemovedException e) { + catch (GridCacheEntryRemovedException ex) { entries.onRemove(key); } } if (!newRows.isEmpty()) { - cctx.offheap().dataStore(entries.part()).rowStore().addRows(newRows, cctx.group().statisticsHolderData()); + cctx.offheap().dataStore(entries.part()).rowStore(). + addRows(newRows, cctx.group().statisticsHolderData()); if (cacheId == CU.UNDEFINED_CACHE_ID) { // Set cacheId before write keys into tree. @@ -472,7 +456,7 @@ public boolean needUpdate(CacheDataRow row) throws GridCacheEntryRemovedExceptio if (cctx.group().persistenceEnabled()) { if (!isStartVer) { if (cctx.atomic()) - update0 = ATOMIC_VER_COMPARATOR.compare(currVer, version()) < 0; + update0 = GridCacheMapEntry.ATOMIC_VER_COMPARATOR.compare(currVer, version()) < 0; else update0 = currVer.compareTo(version()) < 0; } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java index c9dace3ae682d..b572bb40869f0 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/IgniteCacheOffheapManager.java @@ -598,7 +598,7 @@ interface OffheapInvokeClosure extends IgniteTree.InvokeClosure { /** * */ - interface OffheapInvokeAllClosure extends IgniteTree.InvokeAllClosure, IgnitePredicate { + interface OffheapInvokeAllClosure extends IgniteTree.InvokeAllClosure, IgnitePredicate { // boolean preload(); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java index 3dea8074570df..852f2347bbc43 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/preloader/GridDhtPartitionDemander.java @@ -915,59 +915,57 @@ public void preloadEntriesBatch(ClusterNode from, if (entries.isEmpty()) return; - Map cctxMap = new HashMap<>(); + grp.listenerLock().readLock().lock(); - // Map by context. - for (GridCacheEntryInfo info : entries) { - try { - GridCacheContext cctx0 = grp.sharedGroup() ? ctx.cacheContext(info.cacheId()) : grp.singleCacheContext(); + try { + Map cctxs = new HashMap<>(); - if (cctx0 == null) - return; + // Map by context. + for (GridCacheEntryInfo e : entries) { + try { + GridCacheContext cctx0 = grp.sharedGroup() ? ctx.cacheContext(e.cacheId()) : grp.singleCacheContext(); - if (cctx0.isNear()) - cctx0 = cctx0.dhtCache().context(); + if (cctx0 == null) + return; - final GridCacheContext cctx = cctx0; + if (cctx0.isNear()) + cctx0 = cctx0.dhtCache().context(); - if (log.isTraceEnabled()) - log.trace("Rebalancing key [key=" + info.key() + ", part=" + p + ", node=" + from.id() + ']'); + final GridCacheContext cctx = cctx0; - CacheMapEntries batch = cctxMap.get(cctx.cacheId()); + if (log.isTraceEnabled()) + log.trace("Rebalancing key [key=" + e.key() + ", part=" + p + ", node=" + from.id() + ']'); - if (batch == null) { - // todo lock should be called for ALL group - cctx.group().listenerLock().readLock().lock(); + CacheMapEntries batch = + cctxs.computeIfAbsent(cctx.cacheId(), v -> new CacheMapEntries(topVer, p, cctx, true)); - cctxMap.put(cctx.cacheId(), batch = new CacheMapEntries(topVer, p, cctx, true)); + batch.add(e.key(), e.value(), e.expireTime(), e.ttl(), e.version(), DR_PRELOAD); + } + catch (GridDhtInvalidPartitionException ignored) { + if (log.isDebugEnabled()) + log.debug("Partition became invalid during rebalancing (will ignore): " + p); } - - batch.addEntry(info.key(), info.value(), info.expireTime(), info.ttl(), info.version(), DR_PRELOAD); - } - catch (GridDhtInvalidPartitionException ignored) { - if (log.isDebugEnabled()) - log.debug("Partition became invalid during rebalancing (will ignore): " + p); } - } - for (CacheMapEntries batch : cctxMap.values()) { - GridCacheContext cctx = batch.context(); + for (CacheMapEntries batch : cctxs.values()) { + GridCacheContext cctx = batch.context(); - batch.lock(); + batch.lock(); - try { - cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.offheapUpdateClosure()); - } - finally { - batch.unlock(); - - cctx.group().listenerLock().readLock().unlock(); + try { + cctx.offheap().invokeAll(cctx, batch.keys(), batch.part(), batch.offheapUpdateClosure()); + } + finally { + batch.unlock(); - for (GridCacheContext cctx0 : grp.caches()) { - if (cctx0.statisticsEnabled()) - cctx0.cache().metrics0().onRebalanceKeysReceived(batch.size()); + for (GridCacheContext cctx0 : grp.caches()) { + if (cctx0.statisticsEnabled()) + cctx0.cache().metrics0().onRebalanceKeysReceived(batch.size()); + } } } + } finally { + grp.listenerLock().readLock().unlock(); } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java index 7ab4dbe5345aa..10b082a72af28 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/BPlusTree.java @@ -1823,7 +1823,7 @@ public final boolean removex(L row) throws IgniteCheckedException { } /** {@inheritDoc} */ - @Override public void invokeAll(List keys, Object z, InvokeAllClosure c) throws IgniteCheckedException { + @Override public void invokeAll(List keys, Object z, InvokeAllClosure c) throws IgniteCheckedException { throw new UnsupportedOperationException("Not implemented yet"); } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java index f2c66df01f2e1..1ce8e560886e4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/tree/CacheDataTree.java @@ -48,7 +48,6 @@ import org.apache.ignite.internal.stat.IoStatisticsHolder; import org.apache.ignite.internal.util.GridUnsafe; import org.apache.ignite.internal.util.lang.GridCursor; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.T3; import org.apache.ignite.internal.util.typedef.internal.CU; @@ -335,16 +334,12 @@ public CacheDataRowStore rowStore() { /** * todo workaround (fake) implementation only for checking that closure is working properly with preloader. * todo rework - * todo remove flag sorted */ - @Override public void invokeAll( - List keys, - Object x, - InvokeAllClosure c - ) throws IgniteCheckedException { + @Override public void invokeAll(List keys, Object x, InvokeAllClosure c) + throws IgniteCheckedException { checkDestroyed(); - List> rows = new ArrayList<>(keys.size()); + List rows = new ArrayList<>(keys.size()); if (c.fastpath()) { GridCursor cur = find(keys.get(0), keys.get(keys.size() - 1), CacheDataRowAdapter.RowData.FULL); @@ -364,8 +359,8 @@ public CacheDataRowStore rowStore() { while (key == null || key.hashCode() <= oldKey.hashCode()) { if (key != null && key.hashCode() == oldKey.hashCode()) { while (key.hashCode() == oldKey.hashCode()) { - // todo fix and test collision resolution - rows.add(new T2<>(key.equals(oldKey) ? oldRow : null, row)); + // todo test collision resolution + rows.add(key.equals(oldKey) ? oldRow : null); lastRow = null; @@ -378,7 +373,7 @@ public CacheDataRowStore rowStore() { } else { if (row != null) - rows.add(new T2<>(null, row)); + rows.add(null); lastRow = null; @@ -394,16 +389,16 @@ public CacheDataRowStore rowStore() { } if (lastRow != null) - rows.add(new T2<>(key.equals(oldKey) ? oldRow : null, lastRow)); + rows.add(key.equals(oldKey) ? oldRow : null); - while (keyItr.hasNext()) - rows.add(new T2<>(null, keyItr.next())); + for (; keyItr.hasNext(); keyItr.next()) + rows.add(null); } else { for (CacheSearchRow row : keys) { // todo NO_KEY CacheDataRow oldRow = findOne(row, null, CacheDataRowAdapter.RowData.FULL); - rows.add(new T2<>(oldRow, row)); + rows.add(oldRow); } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java index 0d39feee654dd..728b3d98ec576 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteTree.java @@ -21,7 +21,6 @@ import java.util.List; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.util.lang.GridCursor; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.T3; import org.jetbrains.annotations.Nullable; @@ -52,7 +51,7 @@ public interface IgniteTree { * @param c Closure. * @throws IgniteCheckedException If failed. */ - public void invokeAll(List keys, Object x, InvokeAllClosure c) throws IgniteCheckedException; + public void invokeAll(List keys, Object x, InvokeAllClosure c) throws IgniteCheckedException; /** * Returns the value to which the specified key is mapped, or {@code null} if this tree contains no mapping for the @@ -143,20 +142,19 @@ interface InvokeClosure { } /** - * T found row - * L search row + * */ - interface InvokeAllClosure { + interface InvokeAllClosure { /** * - * @param rows Old row -> new row + * @param rows Found/not found data rows. * @throws IgniteCheckedException If failed. */ - void call(@Nullable Collection> rows) throws IgniteCheckedException; + void call(Collection rows) throws IgniteCheckedException; /** * - * @return operation, old row, new row + * @return List of final actions: operation, old row, new row. */ Collection> result(); From 1d10147b3dc53c24bd9a30f3d8374a279bb38d00 Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 26 Mar 2019 14:42:17 +0300 Subject: [PATCH 15/18] IGNITE-7935 Code cleanup. --- .../internal/processors/cache/CacheMapEntries.java | 12 ++++++------ .../cache/persistence/freelist/AbstractFreeList.java | 4 ++-- .../persistence/tree/io/AbstractDataPageIO.java | 8 +++++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java index 132758954a16a..d41f05830d390 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/CacheMapEntries.java @@ -68,7 +68,7 @@ public class CacheMapEntries { private boolean ordered = true; /** */ - private KeyCacheObject temp; + private KeyCacheObject lastKey; /** */ public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheContext cctx, boolean preload) { @@ -80,10 +80,10 @@ public CacheMapEntries(AffinityTopologyVersion topVer, int partId, GridCacheCont /** */ public void add(KeyCacheObject key, CacheObject val, long expTime, long ttl, GridCacheVersion ver, GridDrType drType) { - if (temp != null && ordered && temp.hashCode() >= key.hashCode()) + if (lastKey != null && ordered && lastKey.hashCode() >= key.hashCode()) ordered = false; - CacheMapEntryInfo old = infos.put(temp = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); + CacheMapEntryInfo old = infos.put(lastKey = key, new CacheMapEntryInfo(this, val, expTime, ttl, ver, drType)); assert old == null || GridCacheMapEntry.ATOMIC_VER_COMPARATOR.compare(old.version(), ver) < 0 : "Entry version mismatch: prev=" + old.version() + ", current=" + ver; @@ -139,9 +139,6 @@ public void lock() { if (entry == null) continue; - // todo ensure free space - // todo check obsolete - entry.lockEntry(); if (entry.obsolete()) { @@ -211,6 +208,7 @@ public void unlock() { // Try evict partitions. for (CacheMapEntryInfo info : infos.values()) { GridDhtCacheEntry entry = info.cacheEntry(); + if (entry != null) entry.onUnlock(); } @@ -329,6 +327,8 @@ public UpdateAllClosure(CacheMapEntries entries, GridCacheContext cctx) { } if (!newRows.isEmpty()) { + // cctx.shared().database().ensureFreeSpace(cctx.dataRegion()); + cctx.offheap().dataStore(entries.part()).rowStore(). addRows(newRows, cctx.group().statisticsHolderData()); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 9fabdf0439006..623dfa33d3743 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -344,11 +344,11 @@ private class WriteRowsHandler extends WriteRowHandler { assert written == size : "The object is not fully written into page: pageId=" + pageId + ", written=" + written + ", size=" + row.size(); - - evictionTracker.touchPage(pageId); } } + evictionTracker.touchPage(pageId); + putPage((AbstractDataPageIO)io, pageId, page, pageAddr, statHolder); return COMPLETE; diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java index 4b2d4030fe581..9209e312b32b5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java @@ -1000,9 +1000,11 @@ public void addRows( int written = 0; for (T row : rows) { - boolean fragment = row.size() > maxPayloadSIze; + int size = row.size(); - int payloadSize = row.size() % maxPayloadSIze; + boolean fragment = size > maxPayloadSIze; + + int payloadSize = size % maxPayloadSIze; assert payloadSize <= getFreeSpace(pageAddr) : "can't call addRow if not enough space for the whole row"; @@ -1035,7 +1037,7 @@ public void addRows( setLinkByPageId(row, pageId, cnt); - ++cnt; + cnt += 1; } setDirectCount(pageAddr, cnt); From 78924d9eaab696cc1b53c3a7acbddbe69f4b877c Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Tue, 26 Mar 2019 19:49:12 +0300 Subject: [PATCH 16/18] IGNITE-7935 Large data pages batch updates fix. --- .../freelist/AbstractFreeList.java | 77 +++++++++++++------ .../tree/io/AbstractDataPageIO.java | 8 +- 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 623dfa33d3743..5b9f6439efc1f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -48,6 +48,10 @@ import org.apache.ignite.internal.stat.IoStatisticsHolderNoOp; import org.apache.ignite.internal.util.typedef.internal.U; +import static org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO.SHOW_ITEM; +import static org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO.SHOW_LINK; +import static org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO.SHOW_PAYLOAD_LEN; + /** */ public abstract class AbstractFreeList extends PagesList implements FreeList, ReuseList { @@ -69,6 +73,12 @@ public abstract class AbstractFreeList extends PagesList imp /** */ private static final int MIN_PAGE_FREE_SPACE = 8; + /** */ + private static final int MAX_DATA_ROWS_PER_PAGE = 255; // Item index on data page has 1-byte length. + + /** */ + private static final int MIN_DATA_ROW_SIZE = 39; // 17 (version) + 7 (min key) + 7 (min value) + 8 (expireTime) + /** * Step between buckets in free list, measured in powers of two. * For example, for page size 4096 and 256 buckets, shift is 4 and step is 16 bytes. @@ -610,29 +620,27 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) /** {@inheritDoc} */ @Override public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { - // 1. split into 3 bags - // A. Large objects. - // B1. Tails of large objects - // B2. small objects + int pageSize = pageSize(); // Max bytes per data page. - int maxPayloadSize = pageSize() - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; + int maxPayload = pageSize - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; - int maxRowsPerPage = IndexStorageImpl.MAX_IDX_NAME_LEN; + // On large data pages it is possible to get item index overflow, because it cannot be greater 255. + boolean checkItemIdOverflow = pageSize / MIN_DATA_ROW_SIZE > MAX_DATA_ROWS_PER_PAGE; // Data rows <-> count of pages needed - List largeRows = new ArrayList<>(16); + List largeRows = new ArrayList<>(8); // other objects - List regularRows = new ArrayList<>(16); + List regularRows = new ArrayList<>(8); for (T dataRow : rows) { - if (dataRow.size() < maxPayloadSize) + if (dataRow.size() < maxPayload) regularRows.add(dataRow); else { largeRows.add(dataRow); - int tailSize = dataRow.size() % maxPayloadSize; + int tailSize = dataRow.size() % maxPayload; if (tailSize > 0) regularRows.add(dataRow); @@ -677,38 +685,47 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) while (written != COMPLETE); } - List dataRows = new ArrayList<>(maxRowsPerPage); - - int remainPageSpace = 0; + List dataRows = new ArrayList<>(MAX_DATA_ROWS_PER_PAGE); + AbstractDataPageIO initIo = null; long pageId = 0; + int maxRowsPerPage = 0; + int remainPageSpace = 0; - AbstractDataPageIO initIo = null; + AbstractDataPageIO latestPageIO = ioVersions().latest(); for (int i = 0; i < regularRows.size(); i++) { T row = regularRows.get(i); boolean tail = i == (regularRows.size() - 1); - boolean fragment = row.size() > maxPayloadSize; + int size = row.size(); - int payloadSize = fragment ? (row.size() % maxPayloadSize) + 12 : row.size() + 4; + boolean fragment = size > maxPayload; + + int sizeSetup = fragment ? SHOW_PAYLOAD_LEN | SHOW_LINK | SHOW_ITEM : SHOW_PAYLOAD_LEN | SHOW_ITEM; + + int payloadSize = latestPageIO.getPageEntrySize(fragment ? size % maxPayload : size, sizeSetup); // There is no space left on this page. - if (((remainPageSpace - payloadSize) < 0 || dataRows.size() == maxRowsPerPage) && pageId != 0) { + if (pageId != 0 && ((remainPageSpace - payloadSize) < 0 || dataRows.size() == maxRowsPerPage)) { int written = write(pageId, writeRows, initIo, dataRows, FAIL_I, statHolder); assert written == COMPLETE : written; initIo = null; - remainPageSpace = 0; + pageId = 0; + remainPageSpace = 0; + dataRows.clear(); } dataRows.add(row); if (pageId == 0) { + maxRowsPerPage = MAX_DATA_ROWS_PER_PAGE; + int minBucket = bucket(payloadSize, false) + 1; if (payloadSize != MIN_SIZE_FOR_DATA_PAGE) { @@ -716,7 +733,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) pageId = takeEmptyPage(b, ioVersions(), statHolder); if (pageId != 0L) { - remainPageSpace = (b << shift); //todo + 4, wtf "+4"? + remainPageSpace = (b << shift); break; } @@ -729,15 +746,29 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) if (pageId == 0) { pageId = allocateDataPage(row.partition()); - initIo = ioVersions().latest(); + initIo = latestPageIO; } else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) pageId = initReusedPage(pageId, row.partition(), statHolder); - else + else { pageId = PageIdUtils.changePartitionId(pageId, row.partition()); + if (checkItemIdOverflow) { + long page = pageMem.acquirePage(grpId, pageId, statHolder); + + long pageAddr = PageHandler.readLock(pageMem, grpId, pageId, page, this); + + try { + maxRowsPerPage = MAX_DATA_ROWS_PER_PAGE - latestPageIO.getRowsCount(pageAddr); + } + finally { + PageHandler.readUnlock(pageMem, grpId, pageId, page, pageAddr, this); + } + } + } + if (remainPageSpace == 0) - remainPageSpace = maxPayloadSize; + remainPageSpace = maxPayload; } remainPageSpace -= payloadSize; @@ -746,7 +777,7 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) int written; if (dataRows.size() == 1) { - written = fragment ? row.size() - (row.size() % maxPayloadSize) : 0; + written = fragment ? row.size() - (row.size() % maxPayload) : 0; written = write(pageId, writeRows, initIo, row, written, FAIL_I, statHolder); } else diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java index 9209e312b32b5..5dd6d90d8dde3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java @@ -42,13 +42,13 @@ public abstract class AbstractDataPageIO extends PageIO implements CompactablePageIO { /** */ - private static final int SHOW_ITEM = 0b0001; + public static final int SHOW_ITEM = 0b0001; /** */ - private static final int SHOW_PAYLOAD_LEN = 0b0010; + public static final int SHOW_PAYLOAD_LEN = 0b0010; /** */ - private static final int SHOW_LINK = 0b0100; + public static final int SHOW_LINK = 0b0100; /** */ private static final int FREE_LIST_PAGE_ID_OFF = COMMON_HEADER_END; @@ -148,7 +148,7 @@ private int getPageEntrySize(long pageAddr, int dataOff, int show) { * @param show What elements of data page entry to show in the result. * @return Data page entry size. */ - private int getPageEntrySize(int payloadLen, int show) { + public int getPageEntrySize(int payloadLen, int show) { assert payloadLen > 0 : payloadLen; int res = payloadLen; From 80adb2b0836483a197fcc7972ed028033892ab7c Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 27 Mar 2019 18:50:54 +0300 Subject: [PATCH 17/18] IGNITE-7935 Code cleanup. --- .../cache/persistence/DataStructure.java | 7 +- .../freelist/AbstractFreeList.java | 197 +++++++----------- .../tree/io/AbstractDataPageIO.java | 14 +- .../persistence/tree/util/PageHandler.java | 10 +- 4 files changed, 91 insertions(+), 137 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java index ab9bf86913bdd..6bbe1316b8ca5 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/DataStructure.java @@ -17,7 +17,7 @@ package org.apache.ignite.internal.processors.cache.persistence; -import java.util.Collection; +import java.util.List; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import org.apache.ignite.IgniteCheckedException; @@ -322,10 +322,11 @@ protected final R write( long pageId, PageHandler h, PageIO init, - Collection arg, + List arg, + int intArg, R lockFailed, IoStatisticsHolder statHolder) throws IgniteCheckedException { - return PageHandler.writePageBatch(pageMem, grpId, pageId, this, h, init, wal, null, arg, lockFailed, statHolder); + return PageHandler.writePageBatch(pageMem, grpId, pageId, this, h, init, wal, null, arg, intArg, lockFailed, statHolder); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 5b9f6439efc1f..7f4a30f99e1ab 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -33,7 +33,6 @@ import org.apache.ignite.internal.pagemem.wal.record.delta.DataPageUpdateRecord; import org.apache.ignite.internal.processors.cache.persistence.DataRegion; import org.apache.ignite.internal.processors.cache.persistence.DataRegionMetricsImpl; -import org.apache.ignite.internal.processors.cache.persistence.IndexStorageImpl; import org.apache.ignite.internal.processors.cache.persistence.Storable; import org.apache.ignite.internal.processors.cache.persistence.evict.PageEvictionTracker; import org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO; @@ -48,10 +47,6 @@ import org.apache.ignite.internal.stat.IoStatisticsHolderNoOp; import org.apache.ignite.internal.util.typedef.internal.U; -import static org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO.SHOW_ITEM; -import static org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO.SHOW_LINK; -import static org.apache.ignite.internal.processors.cache.persistence.tree.io.AbstractDataPageIO.SHOW_PAYLOAD_LEN; - /** */ public abstract class AbstractFreeList extends PagesList implements FreeList, ReuseList { @@ -76,9 +71,6 @@ public abstract class AbstractFreeList extends PagesList imp /** */ private static final int MAX_DATA_ROWS_PER_PAGE = 255; // Item index on data page has 1-byte length. - /** */ - private static final int MIN_DATA_ROW_SIZE = 39; // 17 (version) + 7 (min key) + 7 (min value) + 8 (expireTime) - /** * Step between buckets in free list, measured in powers of two. * For example, for page size 4096 and 256 buckets, shift is 4 and step is 16 bytes. @@ -100,6 +92,9 @@ public abstract class AbstractFreeList extends PagesList imp /** */ private final PageEvictionTracker evictionTracker; + /** */ + private final int maxPayloadSize = pageSize() - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; + /** * */ @@ -321,6 +316,7 @@ protected void putPage( * */ private class WriteRowsHandler extends WriteRowHandler { + /** {@inheritDoc} */ @Override public Integer runAll( int cacheId, @@ -329,39 +325,69 @@ private class WriteRowsHandler extends WriteRowHandler { long pageAddr, PageIO io, Boolean walPlc, - Collection args, + List rows, + int writtenCnt, IoStatisticsHolder statHolder ) throws IgniteCheckedException { - int maxPayloadSize = pageSize() - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; - AbstractDataPageIO iox = (AbstractDataPageIO)io; - // todo !! optimize WAL recording - if (iox.getFreeSpace(pageAddr) == maxPayloadSize && !needWalDeltaRecord(pageId, page, null)) { - // todo save links for WAL - iox.addRows(pageMem, pageId, pageAddr, args, pageSize()); - // todo update wal - } - else { - for (T row : args) { - assert iox.getFreeSpace(pageAddr) > 0 : iox.getFreeSpace(pageAddr); + int idx = writtenCnt; + int remainSpace = iox.getFreeSpace(pageAddr); + int remainItems = MAX_DATA_ROWS_PER_PAGE - iox.getRowsCount(pageAddr); + + boolean pageIsEmpty = remainItems == MAX_DATA_ROWS_PER_PAGE; - int size = row.size(); + List rows0 = pageIsEmpty ? new ArrayList<>(8) : null; + while (idx < rows.size() && remainItems > 0) { + T row = rows.get(idx); + + int size = row.size(); + int payloadSize = size % maxPayloadSize; + + // If there is not enough space on page. + if (remainSpace < payloadSize) + break; + + if (pageIsEmpty) + rows0.add(row); + else { int written = size > maxPayloadSize ? - addRowFragment(pageId, page, pageAddr, iox, row, size - (size % maxPayloadSize), size) : + addRowFragment(pageId, page, pageAddr, iox, row, size - payloadSize, size) : addRow(pageId, page, pageAddr, iox, row, size); assert written == size : "The object is not fully written into page: pageId=" + pageId + ", written=" + written + ", size=" + row.size(); } + + remainSpace -= getPageEntrySize(row, iox); + remainItems -= 1; + + idx += 1; } + // Update page counters only once. + if (pageIsEmpty) + iox.addRows(pageMem, pageId, pageAddr, rows0, pageSize()); + + assert idx != writtenCnt; + evictionTracker.touchPage(pageId); putPage((AbstractDataPageIO)io, pageId, page, pageAddr, statHolder); - return COMPLETE; + return idx; + } + + /** */ + private int getPageEntrySize(T row, AbstractDataPageIO io) throws IgniteCheckedException { + int size = row.size(); + + int sizeSetup = size > maxPayloadSize ? + AbstractDataPageIO.SHOW_PAYLOAD_LEN | AbstractDataPageIO.SHOW_LINK | AbstractDataPageIO.SHOW_ITEM : + AbstractDataPageIO.SHOW_PAYLOAD_LEN | AbstractDataPageIO.SHOW_ITEM; + + return io.getPageEntrySize(size % maxPayloadSize, sizeSetup); } } @@ -620,14 +646,6 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) /** {@inheritDoc} */ @Override public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { - int pageSize = pageSize(); - - // Max bytes per data page. - int maxPayload = pageSize - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; - - // On large data pages it is possible to get item index overflow, because it cannot be greater 255. - boolean checkItemIdOverflow = pageSize / MIN_DATA_ROW_SIZE > MAX_DATA_ROWS_PER_PAGE; - // Data rows <-> count of pages needed List largeRows = new ArrayList<>(8); @@ -635,19 +653,18 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) List regularRows = new ArrayList<>(8); for (T dataRow : rows) { - if (dataRow.size() < maxPayload) + int size = dataRow.size(); + + if (size < maxPayloadSize) regularRows.add(dataRow); else { largeRows.add(dataRow); - int tailSize = dataRow.size() % maxPayload; - - if (tailSize > 0) + if (size % maxPayloadSize > 0) regularRows.add(dataRow); } } - // Writing large objects. for (T row : largeRows) { int rowSize = row.size(); @@ -685,106 +702,40 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) while (written != COMPLETE); } - List dataRows = new ArrayList<>(MAX_DATA_ROWS_PER_PAGE); - AbstractDataPageIO initIo = null; - - long pageId = 0; - int maxRowsPerPage = 0; - int remainPageSpace = 0; - - AbstractDataPageIO latestPageIO = ioVersions().latest(); - - for (int i = 0; i < regularRows.size(); i++) { - T row = regularRows.get(i); - - boolean tail = i == (regularRows.size() - 1); - - int size = row.size(); - - boolean fragment = size > maxPayload; + for (int writtenCnt = 0; writtenCnt < regularRows.size(); ) { + T row = regularRows.get(writtenCnt); - int sizeSetup = fragment ? SHOW_PAYLOAD_LEN | SHOW_LINK | SHOW_ITEM : SHOW_PAYLOAD_LEN | SHOW_ITEM; + int size = row.size() % maxPayloadSize; - int payloadSize = latestPageIO.getPageEntrySize(fragment ? size % maxPayload : size, sizeSetup); + int minBucket = bucket(size, false) + 1; - // There is no space left on this page. - if (pageId != 0 && ((remainPageSpace - payloadSize) < 0 || dataRows.size() == maxRowsPerPage)) { - int written = write(pageId, writeRows, initIo, dataRows, FAIL_I, statHolder); + long pageId = 0; - assert written == COMPLETE : written; - - initIo = null; + AbstractDataPageIO initIo = null; - pageId = 0; - remainPageSpace = 0; + if (size != MIN_SIZE_FOR_DATA_PAGE) { + for (int b = REUSE_BUCKET - 1; b >= minBucket; b--) { + pageId = takeEmptyPage(b, ioVersions(), statHolder); - dataRows.clear(); + if (pageId != 0L) + break; + } } - dataRows.add(row); + if (pageId == 0) + pageId = takeEmptyPage(REUSE_BUCKET, ioVersions(), statHolder); if (pageId == 0) { - maxRowsPerPage = MAX_DATA_ROWS_PER_PAGE; - - int minBucket = bucket(payloadSize, false) + 1; - - if (payloadSize != MIN_SIZE_FOR_DATA_PAGE) { - for (int b = REUSE_BUCKET - 1; b >= minBucket; b--) { - pageId = takeEmptyPage(b, ioVersions(), statHolder); - - if (pageId != 0L) { - remainPageSpace = (b << shift); - - break; - } - } - } - - if (pageId == 0) - pageId = takeEmptyPage(REUSE_BUCKET, ioVersions(), statHolder); - - if (pageId == 0) { - pageId = allocateDataPage(row.partition()); - - initIo = latestPageIO; - } - else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) - pageId = initReusedPage(pageId, row.partition(), statHolder); - else { - pageId = PageIdUtils.changePartitionId(pageId, row.partition()); - - if (checkItemIdOverflow) { - long page = pageMem.acquirePage(grpId, pageId, statHolder); - - long pageAddr = PageHandler.readLock(pageMem, grpId, pageId, page, this); - - try { - maxRowsPerPage = MAX_DATA_ROWS_PER_PAGE - latestPageIO.getRowsCount(pageAddr); - } - finally { - PageHandler.readUnlock(pageMem, grpId, pageId, page, pageAddr, this); - } - } - } + pageId = allocateDataPage(row.partition()); - if (remainPageSpace == 0) - remainPageSpace = maxPayload; + initIo = ioVersions().latest(); } + else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) + pageId = initReusedPage(pageId, row.partition(), statHolder); + else + pageId = PageIdUtils.changePartitionId(pageId, row.partition()); - remainPageSpace -= payloadSize; - - if (tail) { - int written; - - if (dataRows.size() == 1) { - written = fragment ? row.size() - (row.size() % maxPayload) : 0; - - written = write(pageId, writeRows, initIo, row, written, FAIL_I, statHolder); - } else - written = write(pageId, writeRows, initIo, dataRows, FAIL_I, statHolder); - - assert written == COMPLETE : written; - } + writtenCnt = write(pageId, writeRows, initIo, regularRows, writtenCnt, FAIL_I, statHolder);; } } diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java index 5dd6d90d8dde3..d047d4714b150 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/io/AbstractDataPageIO.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageIdUtils; @@ -993,10 +994,14 @@ public void addRows( final Collection rows, final int pageSize ) throws IgniteCheckedException { - // todo code duplication (3 times!) int maxPayloadSIze = pageSize - MIN_DATA_PAGE_OVERHEAD; + + int regularSizeFlags = SHOW_PAYLOAD_LEN | SHOW_ITEM; + int fragmentSizeFlags = regularSizeFlags | SHOW_LINK; + int dataOff = pageSize; int cnt = 0; + int written = 0; for (T row : rows) { @@ -1006,11 +1011,7 @@ public void addRows( int payloadSize = size % maxPayloadSIze; - assert payloadSize <= getFreeSpace(pageAddr) : "can't call addRow if not enough space for the whole row"; - - int sizeSetup = fragment ? SHOW_PAYLOAD_LEN | SHOW_LINK | SHOW_ITEM : SHOW_PAYLOAD_LEN | SHOW_ITEM; - - int fullEntrySize = getPageEntrySize(payloadSize, sizeSetup); + int fullEntrySize = getPageEntrySize(payloadSize, fragment ? fragmentSizeFlags : regularSizeFlags); written += fullEntrySize; @@ -1024,7 +1025,6 @@ public void addRows( buf.putShort((short)(payloadSize | FRAGMENTED_FLAG)); buf.putLong(row.link()); - // todo is it 0? writeFragmentData(row, buf, 0, payloadSize); } else diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java index c5e6bda0dadd9..07467f42c166e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/tree/util/PageHandler.java @@ -18,7 +18,7 @@ package org.apache.ignite.internal.processors.cache.persistence.tree.util; import java.nio.ByteBuffer; -import java.util.Collection; +import java.util.List; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.pagemem.PageMemory; import org.apache.ignite.internal.pagemem.PageSupport; @@ -90,7 +90,8 @@ public R runAll( long pageAddr, PageIO io, Boolean walPlc, - Collection args, + List args, + int intArg, IoStatisticsHolder statHolder ) throws IgniteCheckedException { throw new UnsupportedOperationException(); @@ -358,7 +359,8 @@ public static R writePageBatch( PageIO init, IgniteWriteAheadLogManager wal, Boolean walPlc, - Collection args, + List args, + int intArg, R lockFailed, IoStatisticsHolder statHolder ) throws IgniteCheckedException { @@ -383,7 +385,7 @@ public static R writePageBatch( else init = PageIO.getPageIO(pageAddr); - R res = h.runAll(grpId, pageId, page, pageAddr, init, walPlc, args, statHolder); + R res = h.runAll(grpId, pageId, page, pageAddr, init, walPlc, args, intArg, statHolder); ok = true; From bbc57909c93fd1ce8ea9d8f55b4c83ed41313e6a Mon Sep 17 00:00:00 2001 From: pereslegin-pa Date: Wed, 27 Mar 2019 20:48:41 +0300 Subject: [PATCH 18/18] IGNITE-7935 Code cleanup. --- .../freelist/AbstractFreeList.java | 56 ++++++++----------- .../FreeListPreloadWithBatchUpdatesTest.java | 45 ++++++++++----- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java index 7f4a30f99e1ab..bd81d757f03ac 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java @@ -92,9 +92,6 @@ public abstract class AbstractFreeList extends PagesList imp /** */ private final PageEvictionTracker evictionTracker; - /** */ - private final int maxPayloadSize = pageSize() - AbstractDataPageIO.MIN_DATA_PAGE_OVERHEAD; - /** * */ @@ -343,7 +340,7 @@ private class WriteRowsHandler extends WriteRowHandler { T row = rows.get(idx); int size = row.size(); - int payloadSize = size % maxPayloadSize; + int payloadSize = size % MIN_SIZE_FOR_DATA_PAGE; // If there is not enough space on page. if (remainSpace < payloadSize) @@ -352,7 +349,7 @@ private class WriteRowsHandler extends WriteRowHandler { if (pageIsEmpty) rows0.add(row); else { - int written = size > maxPayloadSize ? + int written = size > MIN_SIZE_FOR_DATA_PAGE ? addRowFragment(pageId, page, pageAddr, iox, row, size - payloadSize, size) : addRow(pageId, page, pageAddr, iox, row, size); @@ -383,11 +380,11 @@ private class WriteRowsHandler extends WriteRowHandler { private int getPageEntrySize(T row, AbstractDataPageIO io) throws IgniteCheckedException { int size = row.size(); - int sizeSetup = size > maxPayloadSize ? + int sizeSetup = size > MIN_SIZE_FOR_DATA_PAGE ? AbstractDataPageIO.SHOW_PAYLOAD_LEN | AbstractDataPageIO.SHOW_LINK | AbstractDataPageIO.SHOW_ITEM : AbstractDataPageIO.SHOW_PAYLOAD_LEN | AbstractDataPageIO.SHOW_ITEM; - return io.getPageEntrySize(size % maxPayloadSize, sizeSetup); + return io.getPageEntrySize(size % MIN_SIZE_FOR_DATA_PAGE, sizeSetup); } } @@ -646,43 +643,38 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) /** {@inheritDoc} */ @Override public void insertDataRows(Collection rows, IoStatisticsHolder statHolder) throws IgniteCheckedException { - // Data rows <-> count of pages needed - List largeRows = new ArrayList<>(8); + // Objects that don't fit into a single data page. + List largeRows = new ArrayList<>(); - // other objects + // Ordinary objects and the remaining parts of large objects. List regularRows = new ArrayList<>(8); for (T dataRow : rows) { int size = dataRow.size(); - if (size < maxPayloadSize) + if (size < MIN_SIZE_FOR_DATA_PAGE) regularRows.add(dataRow); else { largeRows.add(dataRow); - if (size % maxPayloadSize > 0) + if (size % MIN_SIZE_FOR_DATA_PAGE > 0) regularRows.add(dataRow); } } for (T row : largeRows) { - int rowSize = row.size(); + int size = row.size(); int written = 0; do { - if (written != 0) - memMetrics.incrementLargeEntriesPages(); - - int remaining = rowSize - written; + int remaining = size - written; - long pageId; - - if (remaining >= MIN_SIZE_FOR_DATA_PAGE) - pageId = takeEmptyPage(REUSE_BUCKET, ioVersions(), statHolder); - else + if (remaining < MIN_SIZE_FOR_DATA_PAGE) break; + long pageId = takeEmptyPage(REUSE_BUCKET, ioVersions(), statHolder); + AbstractDataPageIO initIo = null; if (pageId == 0L) { @@ -698,6 +690,8 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) written = write(pageId, writeRow, initIo, row, written, FAIL_I, statHolder); assert written != FAIL_I; // We can't fail here. + + memMetrics.incrementLargeEntriesPages(); } while (written != COMPLETE); } @@ -705,21 +699,19 @@ else if (PageIdUtils.tag(pageId) != PageIdAllocator.FLAG_DATA) for (int writtenCnt = 0; writtenCnt < regularRows.size(); ) { T row = regularRows.get(writtenCnt); - int size = row.size() % maxPayloadSize; - - int minBucket = bucket(size, false) + 1; + int size = row.size() % MIN_SIZE_FOR_DATA_PAGE; - long pageId = 0; + int minBucket = bucket(size, false); AbstractDataPageIO initIo = null; - if (size != MIN_SIZE_FOR_DATA_PAGE) { - for (int b = REUSE_BUCKET - 1; b >= minBucket; b--) { - pageId = takeEmptyPage(b, ioVersions(), statHolder); + long pageId = 0; - if (pageId != 0L) - break; - } + for (int b = REUSE_BUCKET - 1; b > minBucket; b--) { + pageId = takeEmptyPage(b, ioVersions(), statHolder); + + if (pageId != 0L) + break; } if (pageId == 0) diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java index 61e02bd8c032e..539c0c4fc7e7b 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/database/FreeListPreloadWithBatchUpdatesTest.java @@ -51,45 +51,54 @@ import static org.apache.ignite.IgniteSystemProperties.IGNITE_DATA_STORAGE_BATCH_PAGE_WRITE; import static org.apache.ignite.IgniteSystemProperties.IGNITE_PDS_WAL_REBALANCE_THRESHOLD; +import static org.apache.ignite.configuration.DataStorageConfiguration.DFLT_PAGE_SIZE; /** * */ @RunWith(Parameterized.class) public class FreeListPreloadWithBatchUpdatesTest extends GridCommonAbstractTest { - /** */ - private static final int HDR_SIZE = 8 + 32; - /** */ private static final long DEF_REG_SIZE_INIT = 3400 * 1024 * 1024L; /** */ private static final long DEF_REG_SIZE = 6144 * 1024 * 1024L; + /** */ + private static final int LARGE_PAGE = 16 * 1024; + /** */ private static final String DEF_CACHE_NAME = "some-cache"; /** */ - @Parameterized.Parameters(name = "with atomicity={0} and persistence={1}") + @Parameterized.Parameters(name = " atomicity={0}, persistence={1}, pageSize={2}") public static Iterable setup() { return Arrays.asList(new Object[][]{ - {CacheAtomicityMode.ATOMIC, false}, - {CacheAtomicityMode.ATOMIC, true}, - {CacheAtomicityMode.TRANSACTIONAL, false}, - {CacheAtomicityMode.TRANSACTIONAL, true}, - {CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT, false}, - {CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT, true} + {CacheAtomicityMode.ATOMIC, false, DFLT_PAGE_SIZE}, + {CacheAtomicityMode.ATOMIC, true, DFLT_PAGE_SIZE}, + {CacheAtomicityMode.ATOMIC, false, LARGE_PAGE}, + {CacheAtomicityMode.ATOMIC, true, LARGE_PAGE}, + {CacheAtomicityMode.TRANSACTIONAL, false, DFLT_PAGE_SIZE}, + {CacheAtomicityMode.TRANSACTIONAL, true, DFLT_PAGE_SIZE}, + {CacheAtomicityMode.TRANSACTIONAL, false, LARGE_PAGE}, + {CacheAtomicityMode.TRANSACTIONAL, true, LARGE_PAGE}, + {CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT, false, DFLT_PAGE_SIZE}, + {CacheAtomicityMode.TRANSACTIONAL_SNAPSHOT, true, DFLT_PAGE_SIZE} }); } /** */ - @Parameterized.Parameter() + @Parameterized.Parameter public CacheAtomicityMode cacheAtomicityMode; /** */ @Parameterized.Parameter(1) public boolean persistence; + /** */ + @Parameterized.Parameter(2) + public Integer pageSize; + /** {@inheritDoc} */ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception { IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName); @@ -102,6 +111,7 @@ public static Iterable setup() { DataStorageConfiguration storeCfg = new DataStorageConfiguration(); storeCfg.setDefaultDataRegionConfiguration(def); + storeCfg.setPageSize(pageSize); if (persistence) { storeCfg.setWalMode(WALMode.LOG_ONLY); @@ -250,14 +260,14 @@ public void testBatchHistoricalRebalance() throws Exception { log.info("Updating values on node #1."); - for (int i = 100; i < 1000; i++) { - if (i % 33 == 0) { + for (int i = 100; i < 2000; i++) { + if (i % 3 == 0) { cache.remove(i); srcMap.remove(i); } else { - byte[] bytes = new byte[512]; + byte[] bytes = new byte[ThreadLocalRandom.current().nextInt(16384)]; Arrays.fill(bytes, (byte)1); @@ -266,6 +276,13 @@ public void testBatchHistoricalRebalance() throws Exception { } } + for (int i = 10_000; i < 11_000; i++) { + byte[] bytes = new byte[ThreadLocalRandom.current().nextInt(16384)]; + + srcMap.put(i, bytes); + cache.put(i, bytes); + } + forceCheckpoint(); log.info("Starting node #2.");