Package com.opengamma.core.position.impl

Source Code of com.opengamma.core.position.impl.NonVersionedRedisPositionSource

/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.core.position.impl;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.common.collect.Maps;
import com.opengamma.DataNotFoundException;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.core.change.ChangeManager;
import com.opengamma.core.change.DummyChangeManager;
import com.opengamma.core.position.Portfolio;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.core.position.PositionSource;
import com.opengamma.core.position.Trade;
import com.opengamma.core.security.impl.NonVersionedRedisSecuritySource;
import com.opengamma.core.security.impl.SimpleSecurityLink;
import com.opengamma.id.ExternalId;
import com.opengamma.id.ExternalIdBundle;
import com.opengamma.id.ObjectId;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.GUIDGenerator;
import com.opengamma.util.metric.MetricProducer;

/*
* REDIS DATA STRUCTURES:
* Portfolio Names:
*     Key["PORTFOLIOS"] -> Hash
*        Hash[Name] -> UniqueId for the portfolio
* Portfolio Unique ID Lookups:
*     Key["NAME-"Name] -> Hash
*        Hash[UNIQUE_ID] -> UniqueId for the portfolio
* Portfolio objects themselves:
*     Key["PRT-"UniqueId] -> Hash
*        Hash[NAME] -> Name
*        HASH["ATT-"AttributeName] -> Attribute Value
* Portfolio contents:
*     Key["PRTPOS-"UniqueId] -> Set
*        Each item in the list is a UniqueId for a position
* Positions:
*     Key["POS-"UniqueId] -> Hash
*        Hash[QTY] -> Quantity
*        Hash[SEC] -> ExternalId for the security
*        Hash["ATT-"AttributeName] -> Attribute Value
*
*/

/**
* A lightweight {@link PositionSource} that cannot handle any versioning, and
* which stores all positions and portfolios as Redis-native data structures
* (rather than Fudge encoding).
*/
public class NonVersionedRedisPositionSource implements PositionSource, MetricProducer {
  private static final Logger s_logger = LoggerFactory.getLogger(NonVersionedRedisSecuritySource.class);
 
  /**
   * The default scheme for unique identifiers.
   */
  public static final String IDENTIFIER_SCHEME_DEFAULT = "RedisPos";
 
  private static final String PORTFOLIOS_HASH_KEY_NAME = "PORTFOLIOS";
 
  private final JedisPool _jedisPool;
  private final String _redisPrefix;
  private final String _portfoliosHashKeyName;
  private Timer _getPortfolioTimer = new Timer();
  private Timer _getPositionTimer = new Timer();
  private Timer _portfolioStoreTimer = new Timer();
  private Timer _positionStoreTimer = new Timer();
  private Timer _positionSetTimer = new Timer();
  private Timer _positionAddTimer = new Timer();
 
  public NonVersionedRedisPositionSource(JedisPool jedisPool) {
    this(jedisPool, "");
  }
 
  public NonVersionedRedisPositionSource(JedisPool jedisPool, String redisPrefix) {
    ArgumentChecker.notNull(jedisPool, "jedisPool");
    ArgumentChecker.notNull(redisPrefix, "redisPrefix");
   
    _jedisPool = jedisPool;
    _redisPrefix = redisPrefix.intern();
    _portfoliosHashKeyName = constructallPortfoliosRedisKey();
  }
 
  /**
   * Gets the jedisPool.
   * @return the jedisPool
   */
  protected JedisPool getJedisPool() {
    return _jedisPool;
  }

  /**
   * Gets the redisPrefix.
   * @return the redisPrefix
   */
  protected String getRedisPrefix() {
    return _redisPrefix;
  }

  @Override
  public void registerMetrics(MetricRegistry summaryRegistry, MetricRegistry detailRegistry, String namePrefix) {
    _getPortfolioTimer = summaryRegistry.timer(namePrefix + ".getPortfolio");
    _getPositionTimer = summaryRegistry.timer(namePrefix + ".getPosition");
    _portfolioStoreTimer = summaryRegistry.timer(namePrefix + ".portfolioStore");
    _positionStoreTimer = summaryRegistry.timer(namePrefix + ".positionStore");
    _positionSetTimer = summaryRegistry.timer(namePrefix + ".positionSet");
    _positionAddTimer = summaryRegistry.timer(namePrefix + ".positionAdd");
  }
 
  protected static UniqueId generateUniqueId() {
    return UniqueId.of(IDENTIFIER_SCHEME_DEFAULT, GUIDGenerator.generate().toString());
  }


  // ---------------------------------------------------------------------------------------
  // REDIS KEY MANAGEMENT
  // ---------------------------------------------------------------------------------------
 
  protected final String toRedisKey(String id, String intermediate) {
    StringBuilder sb = new StringBuilder();
    if (!getRedisPrefix().isEmpty()) {
      sb.append(getRedisPrefix());
      sb.append("-");
    }
    sb.append(intermediate);
    sb.append(id);
    String keyText = sb.toString();
    return keyText;
  }
 
  protected final String toRedisKey(UniqueId uniqueId, String intermediate) {
    return toRedisKey(uniqueId.toString(), intermediate);
  }
 
  protected final String toPortfolioRedisKey(UniqueId uniqueId) {
    return toRedisKey(uniqueId, "PRT-");
  }
 
  protected final String toPortfolioPositionsRedisKey(UniqueId uniqueId) {
    return toRedisKey(uniqueId, "PRTPOS-");
  }
 
  protected final String toPositionRedisKey(UniqueId uniqueId) {
    return toRedisKey(uniqueId, "POS-");
  }
 
  protected final String constructallPortfoliosRedisKey() {
    return toRedisKey(PORTFOLIOS_HASH_KEY_NAME, "");
  }
 
  protected final String toPortfolioNameRedisKey(String portfolioName) {
    return toRedisKey(portfolioName, "NAME-");
  }
 
  // ---------------------------------------------------------------------------------------
  // DATA MANIPULATION
  // ---------------------------------------------------------------------------------------
 
  /**
   * Deep store an entire portfolio, including all positions.
   * The portfolio itself is not modified, including setting the unique ID.
   *
   * @param portfolio The portfolio to store.
   * @return the UniqueId of the portfolio.
   */
  public UniqueId storePortfolio(Portfolio portfolio) {
    ArgumentChecker.notNull(portfolio, "portfolio");
    UniqueId uniqueId = null;
   
    try (Timer.Context context = _portfolioStoreTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
       
        uniqueId = storePortfolio(jedis, portfolio);
        storePortfolioNodes(jedis, toPortfolioPositionsRedisKey(uniqueId), portfolio.getRootNode());
       
        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to store portfolio " + portfolio, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to store portfolio " + portfolio, e);
      }
     
    }
   
    return uniqueId;
  }
 
  public UniqueId storePosition(Position position) {
    ArgumentChecker.notNull(position, "position");
    UniqueId uniqueId = null;
   
    try (Timer.Context context = _positionStoreTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
       
        uniqueId = storePosition(jedis, position);
       
        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to store position " + position, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to store position " + position, e);
      }
     
    }
   
    return uniqueId;
  }
 
  /**
   * A special fast-pass method to just update a position quantity, without
   * updating any of the other fields. Results in a single Redis write.
   *
   * @param position The position, which must already be in the source.
   */
  public void updatePositionQuantity(Position position) {
    ArgumentChecker.notNull(position, "position");
   
    try (Timer.Context context = _positionSetTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
       
        String redisKey = toPositionRedisKey(position.getUniqueId());
        jedis.hset(redisKey, "QTY", position.getQuantity().toPlainString());
       
        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to store position " + position, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to store position " + position, e);
      }
     
    }
  }

  /**
   * Store a new position and attach it to the specified portfolio.
   * @param portfolio the existing portfolio. Must already be in this source.
   * @param position the new position to store and attach.
   * @return map of id to position, not-null.
   */
  public Map<String, Position> addPositionToPortfolio(Portfolio portfolio, Position position) {
    return addPositionsToPortfolio(portfolio, Collections.singleton(position));
  }
 
  /**
   * Store a new set of positions and attach it to the specified portfolio.
   * @param portfolio the existing portfolio. Must already be in this source.
   * @param positions the new positions to store and attach.
   * @return map of id to position, not-null.
   */
  public Map<String, Position> addPositionsToPortfolio(Portfolio portfolio, Collection<Position> positions) {
    ArgumentChecker.notNull(portfolio, "portfolio");
    ArgumentChecker.notNull(portfolio.getUniqueId(), "portfolio UniqueId");
    ArgumentChecker.notNull(positions, "position");
   
    Map<String, Position> id2position = Maps.newLinkedHashMap();
    try (Timer.Context context = _positionAddTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
       
        String[] uniqueIdStrings = new String[positions.size()];
        int i = 0;
        for (Position position : positions) {
          String uniqueId = storePosition(jedis, position).toString();
          uniqueIdStrings[i] = uniqueId;
          i++;
          id2position.put(uniqueId, position);
        }
        UniqueId portfolioUniqueId = portfolio.getUniqueId();
        String portfolioPositionsKey = toPortfolioPositionsRedisKey(portfolioUniqueId);
        // NOTE kirk 2013-06-18 -- The following call is a known performance bottleneck.
        // I spent a full day attempting almost every single way I could imagine to
        // figure out what was going on, before I gave up for the time being.
        // When we're running in a far more realistic way we need to second guess
        // it, but it is a known performance issue on large portfolio loading.
        jedis.sadd(portfolioPositionsKey, uniqueIdStrings);
       
        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to store positions " + positions, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to store positions " + positions, e);
      }
    }
    return id2position;
   
  }
 
  protected UniqueId storePortfolio(Jedis jedis, Portfolio portfolio) {
    UniqueId uniqueId = portfolio.getUniqueId();
    if (uniqueId == null) {
      uniqueId = generateUniqueId();
    }
   
    String uniqueIdKey = toPortfolioRedisKey(uniqueId);
    String portfolioNameKey = toPortfolioNameRedisKey(portfolio.getName());
    jedis.hset(portfolioNameKey, "UNIQUE_ID", uniqueId.toString());
   
    jedis.hset(_portfoliosHashKeyName, portfolio.getName(), uniqueId.toString());
   
    jedis.hset(uniqueIdKey, "NAME", portfolio.getName());
   
    for (Map.Entry<String, String> attribute : portfolio.getAttributes().entrySet()) {
      jedis.hset(uniqueIdKey, "ATT-" + attribute.getKey(), attribute.getValue());
    }
   
    return uniqueId;
  }
 
  protected void storePortfolioNodes(Jedis jedis, String redisKey, PortfolioNode node) {
    Set<String> positionUniqueIds = new HashSet<String>();
    for (Position position : node.getPositions()) {
      UniqueId uniqueId = storePosition(jedis, position);
      positionUniqueIds.add(uniqueId.toString());
    }
    if (!positionUniqueIds.isEmpty()) {
      jedis.sadd(redisKey, positionUniqueIds.toArray(new String[0]));
    }
   
    if (!node.getChildNodes().isEmpty()) {
      s_logger.warn("Possible misuse. Portfolio has a deep structure, but this source flattens. Positions being stored flat.");
    }
    for (PortfolioNode childNode : node.getChildNodes()) {
      storePortfolioNodes(jedis, redisKey, childNode);
    }
  }
 
  protected UniqueId storePosition(Jedis jedis, Position position) {
    UniqueId uniqueId = position.getUniqueId();
    if (uniqueId == null) {
      uniqueId = generateUniqueId();
    }
   
    String redisKey = toPositionRedisKey(uniqueId);
    jedis.hset(redisKey, "QTY", position.getQuantity().toPlainString());
    ExternalIdBundle securityBundle = position.getSecurityLink().getExternalId();
    if (securityBundle == null) {
      throw new OpenGammaRuntimeException("Can only store positions with a link to an ExternalId");
    }
    if (securityBundle.size() != 1) {
      s_logger.warn("Bundle {} not exactly one. Possible misuse of this source.", securityBundle);
    }
    ExternalId securityId = securityBundle.iterator().next();
    jedis.hset(redisKey, "SEC", securityId.toString());
   
    for (Map.Entry<String, String> attribute : position.getAttributes().entrySet()) {
      jedis.hset(redisKey, "ATT-" + attribute.getKey(), attribute.getValue());
    }
   
    if ((position.getTrades() != null) && !position.getTrades().isEmpty()) {
      s_logger.warn("Position has trades. This source does not support trades. Possible misuse.");
    }
   
    return uniqueId;
  }
 
  // ---------------------------------------------------------------------------------------
  // QUERIES OUTSIDE OF POSITION SOURCE INTERFACE
  // ---------------------------------------------------------------------------------------
 
  public Portfolio getByName(String portfolioName) {
    ArgumentChecker.notNull(portfolioName, "portfolioName");
   
    Portfolio portfolio = null;
   
    try (Timer.Context context = _getPortfolioTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
        String nameKey = toPortfolioNameRedisKey(portfolioName);
        String uniqueIdString = jedis.hget(nameKey, "UNIQUE_ID");
       
        if (uniqueIdString != null) {
          UniqueId uniqueId = UniqueId.parse(uniqueIdString);
          portfolio = getPortfolioWithJedis(jedis, uniqueId);
        }

        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to get portfolio by name " + portfolioName, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to get portfolio by name " + portfolioName, e);
      }
     
    }
    return portfolio;
  }
 
  public Map<String, UniqueId> getAllPortfolioNames() {
    Map<String, UniqueId> result = new TreeMap<String, UniqueId>();
    Jedis jedis = getJedisPool().getResource();
    try {
      Map<String, String> portfolioNames = jedis.hgetAll(_portfoliosHashKeyName);
      for (Map.Entry<String, String> entry : portfolioNames.entrySet()) {
        result.put(entry.getKey(), UniqueId.parse(entry.getValue()));
      }

      getJedisPool().returnResource(jedis);
    } catch (Exception e) {
      s_logger.error("Unable to get portfolio names", e);
      getJedisPool().returnBrokenResource(jedis);
      throw new OpenGammaRuntimeException("Unable to get portfolio names", e);
    }
    return result;
  }

  // ---------------------------------------------------------------------------------------
  // IMPLEMENTATION OF POSITION SOURCE
  // ---------------------------------------------------------------------------------------
 
  @Override
  public ChangeManager changeManager() {
    return DummyChangeManager.INSTANCE;
  }

  @Override
  public Portfolio getPortfolio(UniqueId uniqueId, VersionCorrection versionCorrection) {
    ArgumentChecker.notNull(uniqueId, "uniqueId");
   
    SimplePortfolio portfolio = null;
   
    try (Timer.Context context = _getPortfolioTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
        portfolio = getPortfolioWithJedis(jedis, uniqueId);
       
        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to get portfolio " + uniqueId, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to get portfolio " + uniqueId, e);
      }
     
    }
   
    if (portfolio == null) {
      throw new DataNotFoundException("Unable to locate portfolio with UniqueId " + uniqueId);
    }
   
    return portfolio;
  }
 
  protected SimplePortfolio getPortfolioWithJedis(Jedis jedis, UniqueId uniqueId) {
    SimplePortfolio portfolio = null;
    String redisKey = toPortfolioRedisKey(uniqueId);
    if (jedis.exists(redisKey)) {
      Map<String, String> hashFields = jedis.hgetAll(redisKey);
     
      portfolio = new SimplePortfolio(hashFields.get("NAME"));
      portfolio.setUniqueId(uniqueId);

      for (Map.Entry<String, String> field : hashFields.entrySet()) {
        if (!field.getKey().startsWith("ATT-")) {
          continue;
        }
        String attributeName = field.getKey().substring(4);
        portfolio.addAttribute(attributeName, field.getValue());
      }
     
      SimplePortfolioNode portfolioNode = new SimplePortfolioNode();
      portfolioNode.setName(portfolio.getName());

      String portfolioPositionsKey = toPortfolioPositionsRedisKey(portfolio.getUniqueId());
      Set<String> positionUniqueIds = jedis.smembers(portfolioPositionsKey);
      for (String positionUniqueId : positionUniqueIds) {
        Position position = getPosition(jedis, UniqueId.parse(positionUniqueId));
        if (position != null) {
          portfolioNode.addPosition(position);
        }
      }
      portfolio.setRootNode(portfolioNode);
    }
    return portfolio;
  }

  @Override
  public Portfolio getPortfolio(ObjectId objectId, VersionCorrection versionCorrection) {
    return getPortfolio(UniqueId.of(objectId, null), null);
  }

  @Override
  public PortfolioNode getPortfolioNode(UniqueId uniqueId, VersionCorrection versionCorrection) {
    throw new UnsupportedOperationException("Trades not supported.");
  }

  @Override
  public Position getPosition(UniqueId uniqueId) {
    ArgumentChecker.notNull(uniqueId, "uniqueId");
   
    SimplePosition position = null;
   
    try (Timer.Context context = _getPositionTimer.time()) {
     
      Jedis jedis = getJedisPool().getResource();
      try {
       
        position = getPosition(jedis, uniqueId);
       
        getJedisPool().returnResource(jedis);
      } catch (Exception e) {
        s_logger.error("Unable to get position " + uniqueId, e);
        getJedisPool().returnBrokenResource(jedis);
        throw new OpenGammaRuntimeException("Unable to get position " + uniqueId, e);
      }
     
    }
   
    if (position == null) {
      throw new DataNotFoundException("Unable to find position with UniqueId " + uniqueId);
    }
   
    return position;
  }

  protected SimplePosition getPosition(Jedis jedis, UniqueId uniqueId) {
    String redisKey = toPositionRedisKey(uniqueId);
    if (!jedis.exists(redisKey)) {
      return null;
    }
    SimplePosition position = new SimplePosition();
    position.setUniqueId(uniqueId);
    Map<String, String> hashFields = jedis.hgetAll(redisKey);
    position.setQuantity(new BigDecimal(hashFields.get("QTY")));
    ExternalId secId = ExternalId.parse(hashFields.get("SEC"));
    SimpleSecurityLink secLink = new SimpleSecurityLink();
    secLink.addExternalId(secId);
    position.setSecurityLink(secLink);
   
    for (Map.Entry<String, String> field : hashFields.entrySet()) {
      if (!field.getKey().startsWith("ATT-")) {
        continue;
      }
      String attributeName = field.getKey().substring(4);
      position.addAttribute(attributeName, field.getValue());
    }
    return position;
  }
 
  @Override
  public Position getPosition(ObjectId objectId, VersionCorrection versionCorrection) {
    return getPosition(UniqueId.of(objectId, null));
  }

  @Override
  public Trade getTrade(UniqueId uniqueId) {
    throw new UnsupportedOperationException("Trades not supported.");
  }

}
TOP

Related Classes of com.opengamma.core.position.impl.NonVersionedRedisPositionSource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.