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

com.gemstone.gemfire.cache.client.internal.PutAllOp Maven / Gradle / Ivy

/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed 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. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.cache.client.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.gemstone.gemfire.cache.CacheClosedException;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.client.ServerOperationException;
import com.gemstone.gemfire.cache.client.internal.AbstractOp.ChunkHandler;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.distributed.internal.ServerLocation;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.cache.CachedDeserializable;
import com.gemstone.gemfire.internal.cache.EventID;
import com.gemstone.gemfire.internal.cache.LocalRegion;
import com.gemstone.gemfire.internal.cache.PutAllPartialResultException;
import com.gemstone.gemfire.internal.cache.PutAllPartialResultException.PutAllPartialResult;
import com.gemstone.gemfire.internal.cache.tier.MessageType;
import com.gemstone.gemfire.internal.cache.tier.sockets.ChunkedMessage;
import com.gemstone.gemfire.internal.cache.tier.sockets.Message;
import com.gemstone.gemfire.internal.cache.tier.sockets.Part;
import com.gemstone.gemfire.internal.cache.tier.sockets.VersionedObjectList;
import com.gemstone.gemfire.internal.offheap.StoredObject;
import com.gemstone.gemfire.internal.shared.Version;

/**
 * Does a region putAll on a server
 * @author darrel
 * @since 5.7
 */
public class PutAllOp {
  
  public static final int FLAG_EMPTY = 0x01;
  public static final int FLAG_CONCURRENCY_CHECKS = 0x02;

  static LogWriterI18n logger = null;
  
  /**
   * Does a region put on a server using connections from the given pool
   * to communicate with the server.
   * @param pool the pool to use to communicate with the server.
   * @param region the name of the region to do the putAll on
   * @param map the Map of keys and values to put
   * @param eventId the event id for this putAll
   * @param skipCallbacks true if no callbacks will be invoked
   */
  public static VersionedObjectList execute(ExecutablePool pool,
                             Region region,
                             Map map,
                             EventID eventId,
                             boolean skipCallbacks,
                             boolean isRetry)
  {
    PutAllOpImpl op = new PutAllOpImpl(pool.getLoggerI18n(), region, map,
        eventId, ((PoolImpl)pool).getPRSingleHopEnabled(), skipCallbacks);
    op.initMessagePart();
    if(isRetry) {
      op.getMessage().setIsRetry();
    }
    return (VersionedObjectList)pool.execute(op);
  }
  
  /**
   * Does a region put on a server using connections from the given pool
   * to communicate with the server.
   * @param pool the pool to use to communicate with the server.
   * @param region the name of the region to do the putAll on
   * @param map the Map of keys and values to put
   * @param eventId the event id for this putAll
   */
  public static VersionedObjectList execute(ExecutablePool pool,
                             Region region,
                             Map map,
                             EventID eventId, 
                             boolean skipCallbacks,
                             int retryAttempts)
  {
    logger = pool.getLoggerI18n();
    ClientMetadataService cms = ((LocalRegion)region).getCache()
        .getClientMetadataService();

    Map serverToFilterMap = cms.getServerToFilterMap(
        map.keySet(), region, true);

    if (serverToFilterMap == null || serverToFilterMap.isEmpty()) {
      AbstractOp op = new PutAllOpImpl(pool.getLoggerI18n(), region, map,
          eventId, ((PoolImpl)pool).getPRSingleHopEnabled(), skipCallbacks);
      op.initMessagePart();
      return (VersionedObjectList)pool.execute(op);
    }

    List callableTasks = constructAndGetPutAllTasks(region, map,
        eventId, skipCallbacks, serverToFilterMap, (PoolImpl)pool);

    if (logger.fineEnabled()) {
      logger.fine("PutAllOp#execute : Number of putAll tasks is :"
          + callableTasks.size());
    }
    HashMap failedServers = new HashMap();
    PutAllPartialResult result = new PutAllPartialResult(map.size());
    try {
      Map results = SingleHopClientExecutor
          .submitPutAll(callableTasks, cms, (LocalRegion)region, failedServers);
      for (Map.Entry entry: results.entrySet()) {
        Object value = entry.getValue();
        if (value instanceof PutAllPartialResultException) {
          PutAllPartialResultException pap = (PutAllPartialResultException)value;
          logger.fine("PutAll SingleHop encountered PutAllPartialResultException exception: "+pap+", failedServers are "+failedServers.keySet());
          result.consolidate(pap.getResult());
        } else {
          if (value != null) {
            VersionedObjectList list = (VersionedObjectList)value;
            result.addKeysAndVersions(list);
          }
        }
      }
    } catch (RuntimeException ex) {
      logger.fine("single-hop putAll encountered unexpected exception: "+ex);
        throw ex;
      }

    if (!failedServers.isEmpty()) {
      if (retryAttempts == 0) {
        throw failedServers.values().iterator().next();
      }

      // if the partial result set doesn't already have keys (for tracking version tags)
      // then we need to gather up the keys that we know have succeeded so far and
      // add them to the partial result set
      if (result.getSucceededKeysAndVersions().size() == 0) {
        // if there're failed servers, we need to save the succeed keys in submitPutAll
        // if retry succeeded, everything is ok, otherwise, the saved "succeeded
        // keys" should be consolidated into PutAllPartialResultException
      // succeedKeySet is used to send back to client in PartialResult case
      // so it's not a must to use LinkedHashSet
      Set succeedKeySet = new LinkedHashSet();
      Set serverSet = serverToFilterMap.keySet();
      for (ServerLocation server : serverSet) {
        if (!failedServers.containsKey(server)) {
          succeedKeySet.addAll(serverToFilterMap.get(server));
        }
      }
  
      // save succeedKeys, but if retries all succeeded, discard the PutAllPartialResult
        result.addKeys(succeedKeySet);
      }
      
      // send maps for the failed servers one by one instead of merging 
      // them into one big map. The reason is, we have to keep the same event
      // ids for each sub map. There is a unit test in PutAllCSDUnitTest for
      // the otherwise case.
      boolean oneSubMapRetryFailed = false;
      Set failedServerSet = failedServers.keySet();
      for (ServerLocation failedServer : failedServerSet) {
        //        Throwable failedServers.values().iterator().next();
        RuntimeException savedRTE = failedServers.get(failedServer);
        if (savedRTE instanceof PutAllPartialResultException) {
          // will not retry for PutAllPartialResultException
          // but it means at least one sub map ever failed 
          oneSubMapRetryFailed = true;
          continue;
        }
        Map newMap = new LinkedHashMap();
        Set keySet = serverToFilterMap.get(failedServer);
        for (Object key : keySet) {
          newMap.put(key, map.get(key));
        }

        try {
          VersionedObjectList v = PutAllOp.execute(pool, region, newMap, eventId, skipCallbacks, true);
          if (v == null) {
            result.addKeys(keySet);
          } else {
            result.addKeysAndVersions(v);
          }
        } catch (PutAllPartialResultException pre) {
          oneSubMapRetryFailed = true;
            logger.fine("Retry failed with PutAllPartialResultException: "+pre+" Before retry: "+result.getKeyListString());
            result.consolidate(pre.getResult());
        } catch (Exception rte) {
          oneSubMapRetryFailed = true;
            Object firstKey = newMap.keySet().iterator().next();
            result.saveFailedKey(firstKey, rte);
          }
      } // for failedServer

      // If all retries succeeded, the PRE in first tries can be ignored
      if (oneSubMapRetryFailed && result.hasFailure()) {
        PutAllPartialResultException pre = new PutAllPartialResultException(result);
        throw pre;
      }
    } // failedServers!=null

    return result.getSucceededKeysAndVersions();
  }
  
  private PutAllOp() {
    // no instances allowed
  }
  
  
  static List constructAndGetPutAllTasks(Region region, final Map map,
      final EventID eventId, 
      boolean skipCallbacks,
      final Map serverToFilterMap,
      final PoolImpl pool) {
    final List tasks = new ArrayList();
    ArrayList servers = new ArrayList(
        serverToFilterMap.keySet());

    if (logger.fineEnabled()) {
      logger.fine("Constructing tasks for the servers" + servers);
    }
    for (ServerLocation server : servers) {
      Set filterSet = serverToFilterMap.get(server);
      Map newKeysValuesMap = new LinkedHashMap();
      // iterator 1: for single hop, both iterator filterSet and newKeysValuesMap
      for (Object key : filterSet) {
        newKeysValuesMap.put(key, map.get(key));
      }
      AbstractOp putAllOp = new PutAllOpImpl(pool.getLoggerI18n(), region,
          newKeysValuesMap, eventId, true, skipCallbacks);

      SingleHopOperationCallable task = new SingleHopOperationCallable(
          new ServerLocation(server.getHostName(), server.getPort()), pool,
          putAllOp,UserAttributes.userAttributes.get());
      tasks.add(task);
    }
    return tasks;
  }

  private static class PutAllOpImpl extends AbstractOp {
    
    private boolean prSingleHopEnabled = false;
    
    private LocalRegion region = null;
    
    private Map map = null;
    private ArrayList keys = null;
    
    /**
     * @throws com.gemstone.gemfire.SerializationException if serialization fails
     */
    public PutAllOpImpl(LogWriterI18n lw, Region region, Map map,
        EventID eventId, boolean prSingleHopEnabled, boolean skipCallbacks) {
      super(lw, MessageType.PUTALL, 5 + (map.size() * 2));
      this.prSingleHopEnabled = prSingleHopEnabled;
      this.region = (LocalRegion)region;
      getMessage().addStringPart(region.getFullPath());
      getMessage().addBytesPart(eventId.calcBytes());
      getMessage().addIntPart(skipCallbacks ? 1 : 0);
      this.map = map;      
    }
    
    @Override
    protected void initMessagePart() {
      int size = map.size();
      int flags = 0;
      if (region.getDataPolicy() == DataPolicy.EMPTY) {
        flags |= FLAG_EMPTY;
      }
      if (region.getConcurrencyChecksEnabled()) {
        flags |= FLAG_CONCURRENCY_CHECKS;
      }
      getMessage().addIntPart(flags);
      getMessage().addIntPart(size);
      this.keys = new ArrayList(size);
      Iterator iterator = map.entrySet().iterator();
      while (iterator.hasNext()) {
        Map.Entry mapEntry = (Map.Entry)iterator.next();
        Object key = mapEntry.getKey();
        this.keys.add(key);
        getMessage().addStringOrObjPart(key);
        Object value = mapEntry.getValue();
        if (value instanceof CachedDeserializable) {
          if (value instanceof StoredObject && !((StoredObject) value).isSerialized()) {
            // it is a byte[]
            getMessage().addObjPart(((StoredObject) value).getDeserializedForReading());
          } else {
            Object cdValue = ((CachedDeserializable)value).getValue();
            if (cdValue instanceof byte[]) {
              getMessage().addRawPart((byte[])cdValue, true);
            } else {
              getMessage().addObjPart(cdValue);
            }
          }
        } else {
          getMessage().addObjPart(value);
        }
      }      
    }
    @Override  
    protected Message createResponseMessage() {
      return new ChunkedMessage(2, Version.CURRENT);
    }
    
    @Override
    protected Object processResponse(Message msg) throws Exception {
      throw new UnsupportedOperationException();
    }
    
    @Override
    protected Object processResponse(final Message msg, final Connection con) throws Exception {
      final LogWriterI18n log = InternalDistributedSystem.getLoggerI18n();
      final VersionedObjectList result = new VersionedObjectList();
      final Exception[] exceptionRef = new Exception[1];
      try {
        processChunkedResponse((ChunkedMessage)msg,
                             "putAll",
                             new ChunkHandler() {
                               public void handle(ChunkedMessage cm) throws Exception {
                                 int numParts = msg.getNumberOfParts();
                                 if (log.fineEnabled()) {
                                   log.fine("putAllOp.processChunkedResponse processing message with " + numParts + " parts");
                                 }
                                 for (int partNo=0; partNo < numParts; partNo++) {
                                   Part part = cm.getPart(partNo);
                                   try {
                                     Object o = part.getObject();
                                     if (log.fineEnabled()) {
                                       log.fine("part("+partNo+") contained " + o);
                                     }
                                     if (o == null) {
                                       // no response is an okay response
                                     } else if (o instanceof byte[]) {
                                       if (prSingleHopEnabled) {
                                         byte[] bytesReceived = part.getSerializedForm();
                                         if (/*bytesReceived.length==1 &&*/ bytesReceived[0] != ClientMetadataService.INITIAL_VERSION) { // nw hop
                                           if (region != null) {
                                             ClientMetadataService cms;
                                             try {
                                               cms = region.getCache().getClientMetadataService();
                                               cms.scheduleGetPRMetaData(region, false,bytesReceived[1]);
                                             }
                                             catch (CacheClosedException e) {
                                             }
                                           }
                                         }
                                       }
                                     } else if (o instanceof Throwable) {
                                       String s = "While performing a remote putAll";
                                       exceptionRef[0] = new ServerOperationException(s, (Throwable)o);
                                     } else {
                                       VersionedObjectList chunk = (VersionedObjectList)o;
                                       chunk.replaceNullIDs(con.getEndpoint().getMemberId());
                                       result.addAll(chunk);
                                     }
                                   } catch(Exception e) {
                                     exceptionRef[0] = new ServerOperationException("Unable to deserialize value" , e);
                                   }
                                 }
                               }
                             });
      } catch (ServerOperationException e) {
        if (e.getCause() instanceof PutAllPartialResultException) {
          PutAllPartialResultException cause = (PutAllPartialResultException)e.getCause(); 
          cause.getSucceededKeysAndVersions().replaceNullIDs(con.getEndpoint().getMemberId());
          throw cause;
        } else {
          throw e;
        }
      }
      if (exceptionRef[0] != null) {
        throw exceptionRef[0];
      } else {
        // v7.0.1: fill in the keys
        if (result.hasVersions() && result.getKeys().isEmpty()) {
          if (log.finerEnabled()) {
            log.finer("setting keys of response to " + this.keys);
          }
          result.setKeys(this.keys);
        }
      }
      return result;
    }
    
    @Override
    protected boolean isErrorResponse(int msgType) {
      return msgType == MessageType.PUT_DATA_ERROR;
    }

    @Override
    protected long startAttempt(ConnectionStats stats) {
      return stats.startPutAll();
    }
    @Override
    protected void endSendAttempt(ConnectionStats stats, long start) {
      stats.endPutAllSend(start, hasFailed());
    }
    @Override
    protected void endAttempt(ConnectionStats stats, long start) {
      stats.endPutAll(start, hasTimedOut(), hasFailed());
    }
  }
  
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy