You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2017/04/18 05:39:08 UTC

[06/46] ignite git commit: ignite-4982 Properly handle case when near node becomes backup

ignite-4982 Properly handle case when near node becomes backup


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/22e2d8cf
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/22e2d8cf
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/22e2d8cf

Branch: refs/heads/ignite-1561-1
Commit: 22e2d8cf3a8e75c9c7c177be72bbfba4e54cb85e
Parents: 49cae05
Author: Konstantin Dudkov <kd...@ya.ru>
Authored: Mon Apr 17 17:21:27 2017 +0300
Committer: sboikov <sb...@gridgain.com>
Committed: Mon Apr 17 17:21:27 2017 +0300

----------------------------------------------------------------------
 .../GridDhtAtomicAbstractUpdateRequest.java     |  14 +
 .../GridDhtAtomicSingleUpdateRequest.java       |  21 +-
 .../dht/atomic/GridDhtAtomicUpdateRequest.java  |  52 +-
 .../distributed/near/GridNearAtomicCache.java   |  16 +-
 .../ignite/internal/util/GridIntList.java       | 587 +++++++++++++++++++
 5 files changed, 674 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/22e2d8cf/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
index 2826215..4ff8484 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicAbstractUpdateRequest.java
@@ -56,6 +56,9 @@ public abstract class GridDhtAtomicAbstractUpdateRequest extends GridCacheMessag
     /** */
     private static final int DHT_ATOMIC_REPLY_WITHOUT_DELAY = 0x10;
 
+    /** */
+    protected static final int DHT_ATOMIC_OBSOLETE_NEAR_KEY_FLAG_MASK = 0x20;
+
     /** Message index. */
     public static final int CACHE_MSG_IDX = nextIndexId();
 
@@ -343,6 +346,17 @@ public abstract class GridDhtAtomicAbstractUpdateRequest extends GridCacheMessag
     public abstract KeyCacheObject key(int idx);
 
     /**
+     * @return Obsolete near cache keys size.
+     */
+    public abstract int obsoleteNearKeysSize();
+
+    /**
+     * @param idx Obsolete near cache key index.
+     * @return Obsolete near cache key.
+     */
+    public abstract KeyCacheObject obsoleteNearKey(int idx);
+
+    /**
      * @param updCntr Update counter.
      * @return Update counter.
      */

http://git-wip-us.apache.org/repos/asf/ignite/blob/22e2d8cf/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicSingleUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicSingleUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicSingleUpdateRequest.java
index 852beec..8931c24 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicSingleUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicSingleUpdateRequest.java
@@ -135,7 +135,7 @@ public class GridDhtAtomicSingleUpdateRequest extends GridDhtAtomicAbstractUpdat
         assert conflictVer == null : conflictVer;
         assert key.partition() >= 0 : key;
 
-        near(false);
+        assert this.key == null;
 
         this.key = key;
         this.val = val;
@@ -162,6 +162,12 @@ public class GridDhtAtomicSingleUpdateRequest extends GridDhtAtomicAbstractUpdat
         assert ttl <= 0 : ttl;
         assert key.partition() >= 0 : key;
 
+        if (this.key != null) {
+            setFlag(true, DHT_ATOMIC_OBSOLETE_NEAR_KEY_FLAG_MASK);
+
+            return;
+        }
+
         near(true);
 
         this.key = key;
@@ -196,6 +202,18 @@ public class GridDhtAtomicSingleUpdateRequest extends GridDhtAtomicAbstractUpdat
     }
 
     /** {@inheritDoc} */
+    @Override public int obsoleteNearKeysSize() {
+        return isFlag(DHT_ATOMIC_OBSOLETE_NEAR_KEY_FLAG_MASK) ? 1 : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public KeyCacheObject obsoleteNearKey(int idx) {
+        assert obsoleteNearKeysSize() == 1 && idx == 0 : idx;
+
+        return key;
+    }
+
+    /** {@inheritDoc} */
     @Override public int partition() {
         int p = key.partition();
 
@@ -304,7 +322,6 @@ public class GridDhtAtomicSingleUpdateRequest extends GridDhtAtomicAbstractUpdat
         prepareMarshalObject(val, cctx);
 
         prepareMarshalObject(prevVal, cctx);
-
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/22e2d8cf/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateRequest.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateRequest.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateRequest.java
index d8e7f24..54b2ae7 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateRequest.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/dht/atomic/GridDhtAtomicUpdateRequest.java
@@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.cache.GridCacheContext;
 import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
 import org.apache.ignite.internal.processors.cache.KeyCacheObject;
 import org.apache.ignite.internal.processors.cache.version.GridCacheVersion;
+import org.apache.ignite.internal.util.GridIntList;
 import org.apache.ignite.internal.util.GridLongList;
 import org.apache.ignite.internal.util.tostring.GridToStringInclude;
 import org.apache.ignite.internal.util.typedef.F;
@@ -92,6 +93,10 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
     @GridDirectCollection(CacheObject.class)
     private List<CacheObject> nearVals;
 
+    /** Obsolete near values. */
+    @GridToStringInclude
+    private GridIntList obsoleteIndexes;
+
     /** Force transform backups flag. */
     private boolean forceTransformBackups;
 
@@ -267,6 +272,15 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
         long expireTime) {
         assert key.partition() >= 0 : key;
 
+        if (hasKey(key)) {
+            if (obsoleteIndexes == null)
+                obsoleteIndexes = new GridIntList();
+
+            obsoleteIndexes.add(keys.indexOf(key));
+
+            return;
+        }
+
         if (nearKeys == null) {
             nearKeys = new ArrayList<>();
 
@@ -334,6 +348,16 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
     }
 
     /** {@inheritDoc} */
+    @Override public int obsoleteNearKeysSize() {
+        return obsoleteIndexes != null ? obsoleteIndexes.size() : 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override public KeyCacheObject obsoleteNearKey(int idx) {
+        return keys.get(obsoleteIndexes.get(idx));
+    }
+
+    /** {@inheritDoc} */
     @Override public boolean hasKey(KeyCacheObject key) {
         return F.contains(keys, key);
     }
@@ -603,24 +627,30 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
                 writer.incrementState();
 
             case 23:
-                if (!writer.writeCollection("prevVals", prevVals, MessageCollectionItemType.MSG))
+                if (!writer.writeMessage("obsoleteIndexes", obsoleteIndexes))
                     return false;
 
                 writer.incrementState();
 
             case 24:
-                if (!writer.writeMessage("ttls", ttls))
+                if (!writer.writeCollection("prevVals", prevVals, MessageCollectionItemType.MSG))
                     return false;
 
                 writer.incrementState();
 
             case 25:
-                if (!writer.writeMessage("updateCntrs", updateCntrs))
+                if (!writer.writeMessage("ttls", ttls))
                     return false;
 
                 writer.incrementState();
 
             case 26:
+                if (!writer.writeMessage("updateCntrs", updateCntrs))
+                    return false;
+
+                writer.incrementState();
+
+            case 27:
                 if (!writer.writeCollection("vals", vals, MessageCollectionItemType.MSG))
                     return false;
 
@@ -731,7 +761,7 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
                 reader.incrementState();
 
             case 23:
-                prevVals = reader.readCollection("prevVals", MessageCollectionItemType.MSG);
+                obsoleteIndexes = reader.readMessage("obsoleteIndexes");
 
                 if (!reader.isLastRead())
                     return false;
@@ -739,7 +769,7 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
                 reader.incrementState();
 
             case 24:
-                ttls = reader.readMessage("ttls");
+                prevVals = reader.readCollection("prevVals", MessageCollectionItemType.MSG);
 
                 if (!reader.isLastRead())
                     return false;
@@ -747,7 +777,7 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
                 reader.incrementState();
 
             case 25:
-                updateCntrs = reader.readMessage("updateCntrs");
+                ttls = reader.readMessage("ttls");
 
                 if (!reader.isLastRead())
                     return false;
@@ -755,6 +785,14 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
                 reader.incrementState();
 
             case 26:
+                updateCntrs = reader.readMessage("updateCntrs");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 27:
                 vals = reader.readCollection("vals", MessageCollectionItemType.MSG);
 
                 if (!reader.isLastRead())
@@ -780,7 +818,7 @@ public class GridDhtAtomicUpdateRequest extends GridDhtAtomicAbstractUpdateReque
 
     /** {@inheritDoc} */
     @Override public byte fieldsCount() {
-        return 27;
+        return 28;
     }
 
     /** {@inheritDoc} */

http://git-wip-us.apache.org/repos/asf/ignite/blob/22e2d8cf/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
index 0deb9bd..422a3fc 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/distributed/near/GridNearAtomicCache.java
@@ -328,13 +328,6 @@ public class GridNearAtomicCache<K, V> extends GridNearCacheAdapter<K, V> {
                             break;
                         }
 
-                        if (req.hasKey(key)) { // Reader became backup.
-                            if (entry.markObsolete(ver))
-                                removeEntry(entry);
-
-                            break;
-                        }
-
                         CacheObject val = req.nearValue(i);
                         EntryProcessor<Object, Object, Object> entryProcessor = req.nearEntryProcessor(i);
 
@@ -390,6 +383,15 @@ public class GridNearAtomicCache<K, V> extends GridNearCacheAdapter<K, V> {
             }
         }
 
+        for (int i = 0; i < req.obsoleteNearKeysSize(); i++) {
+            KeyCacheObject key = req.obsoleteNearKey(i);
+
+            GridCacheEntryEx entry = peekEx(key);
+
+            if (entry != null && entry.markObsolete(ver))
+                removeEntry(entry);
+        }
+
         return nearEvicted;
     }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/22e2d8cf/modules/core/src/main/java/org/apache/ignite/internal/util/GridIntList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/GridIntList.java b/modules/core/src/main/java/org/apache/ignite/internal/util/GridIntList.java
new file mode 100644
index 0000000..1de8106
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/GridIntList.java
@@ -0,0 +1,587 @@
+/*
+ * 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.util;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import org.apache.ignite.internal.util.typedef.F;
+import org.apache.ignite.internal.util.typedef.internal.S;
+import org.apache.ignite.internal.util.typedef.internal.SB;
+import org.apache.ignite.plugin.extensions.communication.Message;
+import org.apache.ignite.plugin.extensions.communication.MessageReader;
+import org.apache.ignite.plugin.extensions.communication.MessageWriter;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Minimal list API to work with primitive ints. This list exists
+ * to avoid boxing/unboxing when using standard list from Java.
+ */
+public class GridIntList implements Message, Externalizable {
+    /** */
+    private static final long serialVersionUID = 0L;
+
+    /** */
+    private int[] arr;
+
+    /** */
+    private int idx;
+
+    /**
+     *
+     */
+    public GridIntList() {
+        // No-op.
+    }
+
+    /**
+     * @param size Size.
+     */
+    public GridIntList(int size) {
+        arr = new int[size];
+        // idx = 0
+    }
+
+    /**
+     * @param arr Array.
+     */
+    public GridIntList(int[] arr) {
+        this.arr = arr;
+
+        idx = arr.length;
+    }
+
+    /**
+     * @param vals Values.
+     * @return List from values.
+     */
+    public static GridIntList asList(int... vals) {
+        if (F.isEmpty(vals))
+            return new GridIntList();
+
+        return new GridIntList(vals);
+    }
+
+    /**
+     * @param arr Array.
+     * @param size Size.
+     */
+    private GridIntList(int[] arr, int size) {
+        this.arr = arr;
+        idx = size;
+    }
+
+    /**
+     * @return Copy of this list.
+     */
+    public GridIntList copy() {
+        if (idx == 0)
+            return new GridIntList();
+
+        return new GridIntList(Arrays.copyOf(arr, idx));
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (!(o instanceof GridIntList))
+            return false;
+
+        GridIntList that = (GridIntList)o;
+
+        if (idx != that.idx)
+            return false;
+
+        if (idx == 0 || arr == that.arr)
+            return true;
+
+        for (int i = 0; i < idx; i++) {
+            if (arr[i] != that.arr[i])
+                return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        int res = 1;
+
+        for (int i = 0; i < idx; i++) {
+            int element  = arr[i];
+            int elementHash = (int)(element ^ (element >>> 32));
+            res = 31 * res + elementHash;
+        }
+
+        return res;
+    }
+
+    /**
+     * @param l List to add all elements of.
+     */
+    public void addAll(GridIntList l) {
+        assert l != null;
+
+        if (l.isEmpty())
+            return;
+
+        if (arr == null)
+            arr = new int[4];
+
+        int len = arr.length;
+
+        while (len < idx + l.size())
+            len <<= 1;
+
+        arr = Arrays.copyOf(arr, len);
+
+        System.arraycopy(l.arr, 0, arr, idx, l.size());
+
+        idx += l.size();
+    }
+
+    /**
+     * Add element to this array.
+     * @param x Value.
+     */
+    public void add(int x) {
+        if (arr == null)
+            arr = new int[4];
+        else if (arr.length == idx)
+            arr = Arrays.copyOf(arr, arr.length << 1);
+
+        arr[idx++] = x;
+    }
+
+    /**
+     * Clears the list.
+     */
+    public void clear() {
+        idx = 0;
+    }
+
+    /**
+     * Gets the last element.
+     *
+     * @return The last element.
+     */
+    public int last() {
+        return arr[idx - 1];
+    }
+
+    /**
+     * Removes and returns the last element of the list. Complementary method to {@link #add(int)} for stack like usage.
+     *
+     * @return Removed element.
+     * @throws NoSuchElementException If the list is empty.
+     */
+    public int remove() throws NoSuchElementException {
+        if (idx == 0)
+            throw new NoSuchElementException();
+
+        return arr[--idx];
+    }
+
+    /**
+     * Returns (possibly reordered) copy of this list, excluding all elements of given list.
+     *
+     * @param l List of elements to remove.
+     * @return New list without all elements from {@code l}.
+     */
+    public GridIntList copyWithout(GridIntList l) {
+        assert l != null;
+
+        if (idx == 0)
+            return new GridIntList();
+
+        if (l.idx == 0)
+            return new GridIntList(Arrays.copyOf(arr, idx));
+
+        int[] newArr = Arrays.copyOf(arr, idx);
+        int newIdx = idx;
+
+        for (int i = 0; i < l.size(); i++) {
+            int rmVal = l.get(i);
+
+            for (int j = 0; j < newIdx; j++) {
+                if (newArr[j] == rmVal) {
+
+                    while (newIdx > 0 && newArr[newIdx - 1] == rmVal)
+                        newIdx--;
+
+                    if (newIdx > 0) {
+                        newArr[j] = newArr[newIdx - 1];
+                        newIdx--;
+                    }
+                }
+            }
+        }
+
+        return new GridIntList(newArr, newIdx);
+    }
+
+    /**
+     * @param i Index.
+     * @return Value.
+     */
+    public int get(int i) {
+        assert i < idx;
+
+        return arr[i];
+    }
+
+    /**
+     * @return Size.
+     */
+    public int size() {
+        return idx;
+    }
+
+    /**
+     * @return {@code True} if this list has no elements.
+     */
+    public boolean isEmpty() {
+        return idx == 0;
+    }
+
+    /**
+     * @param l Element to find.
+     * @return {@code True} if found.
+     */
+    public boolean contains(int l) {
+        for (int i = 0; i < idx; i++) {
+            if (arr[i] == l)
+                return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * @param l List to check.
+     * @return {@code True} if this list contains all the elements of passed in list.
+     */
+    public boolean containsAll(GridIntList l) {
+        for (int i = 0; i < l.size(); i++) {
+            if (!contains(l.get(i)))
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @return {@code True} if there are no duplicates.
+     */
+    public boolean distinct() {
+        for (int i = 0; i < idx; i++) {
+            for (int j = i + 1; j < idx; j++) {
+                if (arr[i] == arr[j])
+                    return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * @param size New size.
+     * @param last If {@code true} the last elements will be removed, otherwise the first.
+     */
+    public void truncate(int size, boolean last) {
+        assert size >= 0 && size <= idx;
+
+        if (size == idx)
+            return;
+
+        if (!last && idx != 0 && size != 0)
+            System.arraycopy(arr, idx - size, arr, 0, size);
+
+        idx = size;
+    }
+
+    /**
+     * Removes element by given index.
+     *
+     * @param i Index.
+     * @return Removed value.
+     */
+    public int removeIndex(int i) {
+        assert i < idx : i;
+
+        int res = arr[i];
+
+        if (i == idx - 1) { // Last element.
+            idx = i;
+        }
+        else {
+            System.arraycopy(arr, i + 1, arr, i, idx - i - 1);
+            idx--;
+        }
+
+        return res;
+    }
+
+    /**
+     * Removes value from this list.
+     *
+     * @param startIdx Index to begin search with.
+     * @param val Value.
+     * @return Index of removed value if the value was found and removed or {@code -1} otherwise.
+     */
+    public int removeValue(int startIdx, int val) {
+        assert startIdx >= 0;
+
+        for (int i = startIdx; i < idx; i++) {
+            if (arr[i] == val) {
+                removeIndex(i);
+
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Removes value from this list.
+     *
+     * @param startIdx Index to begin search with.
+     * @param oldVal Old value.
+     * @param newVal New value.
+     * @return Index of replaced value if the value was found and replaced or {@code -1} otherwise.
+     */
+    public int replaceValue(int startIdx, int oldVal, int newVal) {
+        for (int i = startIdx; i < idx; i++) {
+            if (arr[i] == oldVal) {
+                arr[i] = newVal;
+
+                return i;
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * @return Array copy.
+     */
+    public int[] array() {
+        int[] res = new int[idx];
+
+        System.arraycopy(arr, 0, res, 0, idx);
+
+        return res;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeInt(idx);
+
+        for (int i = 0; i < idx; i++)
+            out.writeInt(arr[i]);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        idx = in.readInt();
+
+        arr = new int[idx];
+
+        for (int i = 0; i < idx; i++)
+            arr[i] = in.readInt();
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        SB b = new SB("[");
+
+        for (int i = 0; i < idx; i++) {
+            if (i != 0)
+                b.a(',');
+
+            b.a(arr[i]);
+        }
+
+        b.a(']');
+
+        return S.toString(GridIntList.class, this, "arr", b);
+    }
+
+    /**
+     * @param in Input to read list from.
+     * @return Grid int list.
+     * @throws IOException If failed.
+     */
+    @Nullable public static GridIntList readFrom(DataInput in) throws IOException {
+        int idx = in.readInt();
+
+        if (idx == -1)
+            return null;
+
+        int[] arr = new int[idx];
+
+        for (int i = 0; i < idx; i++)
+            arr[i] = in.readInt();
+
+        return new GridIntList(arr);
+    }
+
+    /**
+     * @param out Output to write to.
+     * @param list List.
+     * @throws IOException If failed.
+     */
+    public static void writeTo(DataOutput out, @Nullable GridIntList list) throws IOException {
+        out.writeInt(list != null ? list.idx : -1);
+
+        if (list != null) {
+            for (int i = 0; i < list.idx; i++)
+                out.writeInt(list.arr[i]);
+        }
+    }
+
+    /**
+     * @param to To list.
+     * @param from From list.
+     * @return To list (passed in or created).
+     */
+    public static GridIntList addAll(@Nullable GridIntList to, GridIntList from) {
+        if (to == null) {
+            GridIntList res = new GridIntList(from.size());
+
+            res.addAll(from);
+
+            return res;
+        }
+        else {
+            to.addAll(from);
+
+            return to;
+        }
+    }
+
+    /**
+     * Sorts this list.
+     * Use {@code copy().sort()} if you need a defensive copy.
+     *
+     * @return {@code this} For chaining.
+     */
+    public GridIntList sort() {
+        if (idx > 1)
+            Arrays.sort(arr, 0, idx);
+
+        return this;
+    }
+
+    /**
+     * Removes given number of elements from the end. If the given number of elements is higher than
+     * list size, then list will be cleared.
+     *
+     * @param cnt Count to pop from the end.
+     */
+    public void pop(int cnt) {
+        assert cnt >= 0 : cnt;
+
+        if (idx < cnt)
+            idx = 0;
+        else
+            idx -= cnt;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onAckReceived() {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean writeTo(ByteBuffer buf, MessageWriter writer) {
+        writer.setBuffer(buf);
+
+        if (!writer.isHeaderWritten()) {
+            if (!writer.writeHeader(directType(), fieldsCount()))
+                return false;
+
+            writer.onHeaderWritten();
+        }
+
+        switch (writer.state()) {
+            case 0:
+                if (!writer.writeIntArray("arr", arr))
+                    return false;
+
+                writer.incrementState();
+
+            case 1:
+                if (!writer.writeInt("idx", idx))
+                    return false;
+
+                writer.incrementState();
+
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean readFrom(ByteBuffer buf, MessageReader reader) {
+        reader.setBuffer(buf);
+
+        if (!reader.beforeMessageRead())
+            return false;
+
+        switch (reader.state()) {
+            case 0:
+                arr = reader.readIntArray("arr");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+            case 1:
+                idx = reader.readInt("idx");
+
+                if (!reader.isLastRead())
+                    return false;
+
+                reader.incrementState();
+
+        }
+
+        return reader.afterMessageRead(GridIntList.class);
+    }
+
+    /** {@inheritDoc} */
+    @Override public short directType() {
+        return 85;
+    }
+
+    /** {@inheritDoc} */
+    @Override public byte fieldsCount() {
+        return 2;
+    }    
+}