/** * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.master.position.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import com.opengamma.id.ObjectId; import com.opengamma.id.UniqueId; import com.opengamma.master.impl.AbstractQuerySplittingMaster; import com.opengamma.master.position.ManageableTrade; import com.opengamma.master.position.PositionDocument; import com.opengamma.master.position.PositionHistoryRequest; import com.opengamma.master.position.PositionHistoryResult; import com.opengamma.master.position.PositionMaster; import com.opengamma.master.position.PositionSearchRequest; import com.opengamma.master.position.PositionSearchResult; import com.opengamma.util.paging.Paging; import com.opengamma.util.paging.PagingRequest; /** * A {@link PositionMaster} implementation that divides search operations into a number of smaller operations to pass to the underlying. This is intended for use with some database backed position * masters where performance decreases, or becomes unstable, with large queries. */ public class QuerySplittingPositionMaster extends AbstractQuerySplittingMaster<PositionDocument, PositionMaster> implements PositionMaster { /** * The maximum size of request to pass to {@link PositionMaster#search}, zero or negative for no limit. */ private int _maxSearchRequest; /** * Creates a new instance wrapping the underlying with default properties. * * @param underlying the underlying position master to satisfy the requests, not null */ public QuerySplittingPositionMaster(final PositionMaster underlying) { super(underlying); } /** * Returns the maximum number of items to pass to the {@link PositionMaster#search} method in each call. * * @return the current limit, zero or negative if none */ public int getMaxSearchRequest() { return _maxSearchRequest; } /** * Sets the maximum number of items to pass to the {@link PositionMaster#search} method in each call. * * @param maxSearchRequest the new limit, zero or negative if none */ public void setMaxSearchRequest(final int maxSearchRequest) { _maxSearchRequest = maxSearchRequest; } protected Collection<PositionSearchRequest> splitSearchRequest(final PositionSearchRequest request) { if (request.getPositionObjectIds() == null) { // Can only split requests with multiple object ids return null; } if (!PagingRequest.ALL.equals(request.getPagingRequest()) && !PagingRequest.NONE.equals(request.getPagingRequest())) { // Can only split requests with no paging return null; } int chunkSize = getMaxSearchRequest(); final int count = request.getPositionObjectIds().size(); if ((chunkSize <= 0) || (chunkSize >= count)) { // Request too small, or splitting is disabled return null; } int chunks = (count + chunkSize - 1) / chunkSize; final Collection<PositionSearchRequest> requests = new ArrayList<PositionSearchRequest>(); final Iterator<ObjectId> positions = request.getPositionObjectIds().iterator(); for (int i = 0; i < count;) { chunkSize = (count - i) / (chunks--); final PositionSearchRequest subRequest = request.clone(); subRequest.getPositionObjectIds().clear(); for (int j = 0; (j < chunkSize) && positions.hasNext(); j++) { subRequest.addPositionObjectId(positions.next()); } requests.add(subRequest); i += chunkSize; } return requests; } protected void mergeSplitSearchResult(final PositionSearchResult mergeWith, final PositionSearchResult result) { final Collection<PositionDocument> documents = result.getDocuments(); mergeWith.getDocuments().addAll(documents); mergeWith.setPaging(Paging.of(PagingRequest.ALL, ((mergeWith.getPaging() != null) ? mergeWith.getPaging().getTotalItems() : 0) + documents.size())); mergeWith.setVersionCorrection(result.getVersionCorrection()); } protected PositionSearchResult callSplitSearchRequest(final Collection<PositionSearchRequest> requests) { final PositionSearchResult result = new PositionSearchResult(); for (PositionSearchRequest request : requests) { mergeSplitSearchResult(result, getUnderlying().search(request)); } return result; } // PositionMaster /** * When splitting is enabled, and the request is for more positions than the split size, two or more requests are made to the underlying master. {@inheritDoc} */ @Override public PositionSearchResult search(final PositionSearchRequest request) { if (canSplit()) { final Collection<PositionSearchRequest> requests = splitSearchRequest(request); if (requests == null) { // Small query pass-through return getUnderlying().search(request); } else { // Multiple queries return callSplitSearchRequest(requests); } } else { // Splitting disabled return getUnderlying().search(request); } } @Override public PositionHistoryResult history(final PositionHistoryRequest request) { return getUnderlying().history(request); } @Override public ManageableTrade getTrade(final UniqueId tradeId) { return getUnderlying().getTrade(tradeId); } }