/** @throws Exception If failed. */
  public void testCreateFileColocated() throws Exception {
    GridGgfsPath path = new GridGgfsPath("/colocated");

    UUID uuid = UUID.randomUUID();

    GridUuid affKey;

    long idx = 0;

    while (true) {
      affKey = new GridUuid(uuid, idx);

      if (grid(0).mapKeyToNode(DATA_CACHE_NAME, affKey).id().equals(grid(0).localNode().id()))
        break;

      idx++;
    }

    try (GridGgfsOutputStream out = fs.create(path, 1024, true, affKey, 0, 1024, null)) {
      // Write 5M, should be enough to test distribution.
      for (int i = 0; i < 15; i++) out.write(new byte[1024 * 1024]);
    }

    GridGgfsFile info = fs.info(path);

    Collection<GridGgfsBlockLocation> affNodes = fs.affinity(path, 0, info.length());

    assertEquals(1, affNodes.size());
    Collection<UUID> nodeIds = F.first(affNodes).nodeIds();

    assertEquals(1, nodeIds.size());
    assertEquals(grid(0).localNode().id(), F.first(nodeIds));
  }
/**
 * Eviction policy based on {@code First In First Out (FIFO)} algorithm. This implementation is very
 * efficient since it does not create any additional table-like data structures. The {@code FIFO}
 * ordering information is maintained by attaching ordering metadata to cache entries.
 */
public class GridCacheFifoEvictionPolicy<K, V>
    implements GridCacheEvictionPolicy<K, V>, GridCacheFifoEvictionPolicyMBean {
  /** Tag. */
  private final String meta = UUID.randomUUID().toString();

  /** Maximum size. */
  private volatile int max = GridCacheConfiguration.DFLT_CACHE_SIZE;

  /** FIFO queue. */
  private final ConcurrentLinkedDeque8<GridCacheEntry<K, V>> queue = new ConcurrentLinkedDeque8<>();

  /** Constructs FIFO eviction policy with all defaults. */
  public GridCacheFifoEvictionPolicy() {
    // No-op.
  }

  /**
   * Constructs FIFO eviction policy with maximum size. Empty entries are allowed.
   *
   * @param max Maximum allowed size of cache before entry will start getting evicted.
   */
  public GridCacheFifoEvictionPolicy(int max) {
    A.ensure(max > 0, "max > 0");

    this.max = max;
  }

  /**
   * Gets maximum allowed size of cache before entry will start getting evicted.
   *
   * @return Maximum allowed size of cache before entry will start getting evicted.
   */
  @Override
  public int getMaxSize() {
    return max;
  }

  /**
   * Sets maximum allowed size of cache before entry will start getting evicted.
   *
   * @param max Maximum allowed size of cache before entry will start getting evicted.
   */
  @Override
  public void setMaxSize(int max) {
    A.ensure(max > 0, "max > 0");

    this.max = max;
  }

  /** {@inheritDoc} */
  @Override
  public int getCurrentSize() {
    return queue.size();
  }

  /** {@inheritDoc} */
  @Override
  public String getMetaAttributeName() {
    return meta;
  }

  /**
   * Gets read-only view on internal {@code FIFO} queue in proper order.
   *
   * @return Read-only view ono internal {@code 'FIFO'} queue.
   */
  public Collection<GridCacheEntry<K, V>> queue() {
    return Collections.unmodifiableCollection(queue);
  }

  /** {@inheritDoc} */
  @Override
  public void onEntryAccessed(boolean rmv, GridCacheEntry<K, V> entry) {
    if (!rmv) {
      if (!entry.isCached()) return;

      // Shrink only if queue was changed.
      if (touch(entry)) shrink();
    } else {
      Node<GridCacheEntry<K, V>> node = entry.removeMeta(meta);

      if (node != null) queue.unlinkx(node);
    }
  }

  /**
   * @param entry Entry to touch.
   * @return {@code True} if queue has been changed by this call.
   */
  private boolean touch(GridCacheEntry<K, V> entry) {
    Node<GridCacheEntry<K, V>> node = entry.meta(meta);

    // Entry has not been enqueued yet.
    if (node == null) {
      while (true) {
        node = queue.offerLastx(entry);

        if (entry.putMetaIfAbsent(meta, node) != null) {
          // Was concurrently added, need to clear it from queue.
          queue.unlinkx(node);

          // Queue has not been changed.
          return false;
        } else if (node.item() != null) {
          if (!entry.isCached()) {
            // Was concurrently evicted, need to clear it from queue.
            queue.unlinkx(node);

            return false;
          }

          return true;
        }
        // If node was unlinked by concurrent shrink() call, we must repeat the whole cycle.
        else if (!entry.removeMeta(meta, node)) return false;
      }
    }

    // Entry is already in queue.
    return false;
  }

  /** Shrinks FIFO queue to maximum allowed size. */
  private void shrink() {
    int max = this.max;

    int startSize = queue.sizex();

    for (int i = 0; i < startSize && queue.sizex() > max; i++) {
      GridCacheEntry<K, V> entry = queue.poll();

      if (entry == null) break;

      if (!entry.evict()) {
        entry.removeMeta(meta);

        touch(entry);
      }
    }
  }

  /**
   * Checks entry for empty value.
   *
   * @param entry Entry to check.
   * @return {@code True} if entry is empty.
   */
  private boolean empty(GridCacheEntry<K, V> entry) {
    try {
      return entry.peek(F.asList(GLOBAL)) == null;
    } catch (GridException e) {
      U.error(null, e.getMessage(), e);

      assert false : "Should never happen: " + e;

      return false;
    }
  }

  /** {@inheritDoc} */
  @Override
  public String toString() {
    return S.toString(GridCacheFifoEvictionPolicy.class, this);
  }
}