All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.geode.redis.internal.executor.list.ListExecutor Maven / Gradle / Ivy

Go to download

Apache Geode provides a database-like consistency model, reliable transaction processing and a shared-nothing architecture to maintain very low latency performance with high concurrency processing

There is a newer version: 1.15.1
Show newest version
/*
 * 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.geode.redis.internal.executor.list;

import java.util.List;

import org.apache.geode.cache.Region;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.redis.internal.ByteArrayWrapper;
import org.apache.geode.redis.internal.ExecutionHandlerContext;
import org.apache.geode.redis.internal.RedisDataType;
import org.apache.geode.redis.internal.executor.AbstractExecutor;


public abstract class ListExecutor extends AbstractExecutor {

  protected static final int LIST_EMPTY_SIZE = 2;

  protected static enum ListDirection {
    LEFT, RIGHT
  };

  protected final static QueryService getQueryService() {
    return GemFireCacheImpl.getInstance().getQueryService();
  }

  @SuppressWarnings("unchecked")
  @Override
  protected Region getOrCreateRegion(ExecutionHandlerContext context,
      ByteArrayWrapper key, RedisDataType type) {
    return (Region) context.getRegionProvider().getOrCreateRegion(key,
        type, context);
  }

  @SuppressWarnings("unchecked")
  protected Region getRegion(ExecutionHandlerContext context,
      ByteArrayWrapper key) {
    return (Region) context.getRegionProvider().getRegion(key);
  }

  /**
   * Helper method to be used by the push commands to push elements onto a list. Because our current
   * setup requires non trivial code to push elements in to a Region, I wanted all the push code to
   * reside in one place.
   * 
   * @param key Name of the list
   * @param commandElems Pieces of the command, this is where the elements that need to be pushed
   *        live
   * @param startIndex The index to start with in the commandElems list, inclusive
   * @param endIndex The index to end with in the commandElems list, exclusive
   * @param keyRegion Region of list
   * @param pushType ListDirection.LEFT || ListDirection.RIGHT
   * @param context Context of this push
   */
  protected void pushElements(ByteArrayWrapper key, List commandElems, int startIndex,
      int endIndex, Region keyRegion, ListDirection pushType, ExecutionHandlerContext context) {

    String indexKey = pushType == ListDirection.LEFT ? "head" : "tail";
    String oppositeKey = pushType == ListDirection.RIGHT ? "head" : "tail";
    Integer index = (Integer) keyRegion.get(indexKey);
    Integer opp = (Integer) keyRegion.get(oppositeKey);
    if (index != opp)
      index += pushType == ListDirection.LEFT ? -1 : 1; // Subtract index if left push, add if right
                                                        // push

    /**
     * Multi push command
     * 
     * For every element that needs to be added
     */

    for (int i = startIndex; i < endIndex; i++) {
      byte[] value = commandElems.get(i);
      ByteArrayWrapper wrapper = new ByteArrayWrapper(value);

      /**
       * 
       * First, use the start index to attempt to insert the value into the Region
       * 
       */

      Object oldValue;
      do {
        oldValue = keyRegion.putIfAbsent(index, wrapper);
        if (oldValue != null) {
          index += pushType == ListDirection.LEFT ? -1 : 1; // Subtract index if left push, add if
                                                            // right push
        }
      } while (oldValue != null);

      /**
       * 
       * Next, update the index in the meta data region. Keep trying to replace the existing index
       * unless the index is further out than previously inserted, that's ok. Example below:
       * 
       * ********************** LPUSH/LPUSH *************************** Push occurring at the same
       * time, further index update first | This push | | | | V V [-4] [-3] [-2] [-1] [0] [1] [2]
       * 
       * In this case, -4 would already exist in the meta data region, therefore we do not try to
       * put -3 in the meta data region because a further index is already there.
       * ***************************************************************
       * 
       * Another example
       * 
       * ********************** LPUSH/LPOP ***************************** This push | Simultaneous
       * LPOP, meta data head index already updated to -2 | | | | V V [-4] [X] [-2] [-1] [0] [1] [2]
       * 
       * In this case, -2 would already exist in the meta data region, but we need to make sure the
       * element at -4 is visible to all other threads so we will attempt to change the index to -4
       * as long as it is greater than -4
       * ***************************************************************
       * 
       */

      boolean indexSet = false;
      do {
        Integer existingIndex = (Integer) keyRegion.get(indexKey);
        if ((pushType == ListDirection.RIGHT && existingIndex < index)
            || (pushType == ListDirection.LEFT && existingIndex > index))
          indexSet = keyRegion.replace(indexKey, existingIndex, index);
        else
          break;
      } while (!indexSet);

    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy