Как вы получаете доступ к связанным записям, размещенным в Hazelcast с привязкой к данным?

Я пытаюсь использовать функцию уменьшения карты Hazelcast для выполнения агрегированной операции, которая требует доступа к совместно размещенным записям. Совместное размещение контролируется с помощью привязки данных.

Представьте себе классическую модель customer / order, используемую в Документация Hazelcast о сродстве данных. В моем примере я хочу вернуть сводку по клиентам, в которой есть клиент и сумма всех их заказов, например, с учетом этого набора данных:

customer_id | name
------------------
1           | Dave
2           | Kate


order_id | customer_id | value
------------------------------
1        | 1           | 5
2        | 1           | 10
3        | 2           | 12

Я хочу вернуться:

customer_id | name | value
--------------------------
1           | Dave | 15
2           | Kate | 12

Это достаточно просто, однако причина использования сродства данных заключается в том, чтобы иметь возможность выполнять логику суммирования в соответствующем разделе, содержащем данные, просто получая все заказы в этом разделе и, следовательно, избегая любой перекрестной связи JVM.

Итак, мой вопрос из Mapper или аналогичного , как получить совместно размещенные записи в другом кэше?

РЕДАКТИРОВАТЬ:

После ответа и комментариев @noctarius вот некоторый код (я постарался сделать его как можно короче), который выделяет точку, в которой мне нужны только заказы из текущего раздела.

Класс ключа порядка выглядит так:

public class OrderKey implements PartitionAware<CustomerIdentity>
{
  ...

  @Override
  public CustomerIdentity getPartitionKey()
  {
    return this.customerIdentity; 
  }

  ...
}

И Mapper вот так:

public class OrderSumMapper implements Mapper<CustomerKey, Customer, CustomerKey, CustomerOrderTotal>, HazelcastInstanceAware
{
  ...

  @Override
  public void map(CustomerKey customerKey, Customer customer, Context<CustomerKey, CustomerOrderTotal> context)
  {
    Predicate ordersForCustomer = new OrdersForCustomerPredicate(customerKey);

    int totalValue = 0;

    //******************************************************************
    //
    // Given orders are co-located with the customer, how do you ensure 
    // this call to get the orders only runs in the current partition?
    //
    //******************************************************************

    for (Order order : hazelcastInstance.getMap("orders").values(ordersForCustomer))
    {
      totalValue += order.getValue();
    }

    context.emit(customerKey, new CustomerOrderTotal(customer, total));
  }

  ...
}

Выделенный вызов hazelcastInstance.getMap("orders").values(ordersForCustomer) обычно попадает во все узлы в кластере, но поскольку данные размещены в одном месте, это лишние накладные расходы.

Итак, вернемся к исходному вопросу: как я получаю такие заказы, что возвращаются только те, которые находятся в текущем разделе?


person Nick Holt    schedule 27.06.2014    source источник
comment
Для тех, кто знаком с Coherence, вы могли бы добиться этого с помощью агрегатора и получить совместно расположенные записи через вспомогательную карту.   -  person Nick Holt    schedule 27.06.2014


Ответы (2)


Вы просто вставляете HazelcastInstance текущего узла в свой Mapper и извлекаете вторую структуру данных для чтения данных.

См. Базовый пример здесь: https://github.com/noctarius/hazelcast-mapreduce-presentation/blob/master/src/main/java/com/hazelcast/examples/tutorials/impl/SalaryMapper.java

person noctarius    schedule 06.07.2014
comment
Поправьте меня, если я ошибаюсь - ваш код работает, потому что вы используете один и тот же ключ для Person и SalaryYear? Как бы вы получили доступ к SalaryYear, если бы они хранились несколько лет с использованием составного ключа email и year? - person Nick Holt; 08.07.2014
comment
Почему это должно иметь какое-то отношение к ключу? Вы можете использовать любой ключ, если хотите получить другое значение с другой карты. Это независимые структуры данных. Если вы спрашиваете, потому что согласованные снимки разделов еще не реализованы, но скоро будут. В этом случае у вас будет согласованное представление обо всех структурах данных в одном разделе в определенный момент времени. - person noctarius; 08.07.2014
comment
Ключ важен, потому что ваше сопоставление работает с кешем, содержащим основные значения - клиенты в моем случае, люди в вашем. Это означает, что в моем случае, когда я хочу получить все заказы клиентов в данном разделе, у меня нет полного ключа, только идентификатор клиента. Я думаю, у вас была бы такая же проблема, если бы у вас было несколько лет на каждого человека. Мне интересно, можно ли этого достичь с помощью PartitionAware Predicate - вы знаете, работает ли это? - person Nick Holt; 08.07.2014
comment
У меня все еще нет вашей проблемы, почему бы просто не использовать MultiMap для заказов и просто не получить это внутри Mapper? Думаю, я не понимаю вашей проблемы, поэтому, вероятно, добавлю демонстрационный код. - person noctarius; 09.07.2014
comment
MultiMap будет удовлетворять только одному варианту использования, однако мне нужна карта заказов, привязанная к их составным идентификаторам (идентификатор клиента и заказа). Затем я хочу сопоставить сокращение с клиентами и получить Mapper доступ только к тем заказам в данном разделе, которые расположены вместе с клиентами. Я добавлю код и выделю момент, когда мне нужны только заказы в данном разделе. - person Nick Holt; 10.07.2014
comment
Ах, хорошо, я вижу проблему. На самом деле это хороший вопрос. С нетерпением жду примера кода, я почти уверен, что мы сможем найти решение этой проблемы :) - person noctarius; 10.07.2014
comment
Да, тонкий. Я довольно часто использовал Coherence, и это одно из значительных преимуществ в производительности, которые вы получаете от использования распределенного кеша - вы подталкиваете обработку к данным, а не подтягиваете данные к обработке. Однако во всех случаях, кроме самых простых, вам часто требуется доступ к связанным данным, и если вы выполняете для этого процесс или сетевые переходы, вы теряете производительность. После некоторой игры я разработал способ получить только данные в текущем разделе (и беззастенчиво ответил на свой вопрос) - person Nick Holt; 10.07.2014
comment
Полагаю, вы использовали какой-то внутренний API, не так ли? - person noctarius; 11.07.2014

Я разработал это, и, надеюсь, это окажется полезным для других (и поэтому я беззастенчиво отвечаю и принимаю свой вопрос).

После некоторых экспериментов можно получить доступ к объектам, хранящимся на другой карте в разделе, из Mapper, запущенного в этом разделе.

Во-первых, Mapper реализует NodeAware, который заставляет Hazelcast вставлять ссылку на Node, в котором работает Mapper.

Когда у вас есть Node, вы можете написать такой метод для доступа к данным на других картах в данном разделе, например:

private Collection<Order> getCustomerOrders(CustomerKey customerKey)
{
  List<Order> orders = new ArrayList<>();

  MapService mapService = node.getClusterService().getNodeEngine().getService(MapService.SERVICE_NAME);

  RecordStore recordStore = mapService.getRecordStore(node.getPartitionService().getPartitionId(customerKey), "orders");

  for (Data key : recordStore.keySet())
  {
    OrderKey orderKey = mapService.getSerializationService().toObject(key);

    if (customerKey.equals(orderKey.getCustomerKey()))
    {
      orders.add(mapService.getSerializationService().toObject(recordStore.get(key)));
    }
  }

  return orders;
}

Есть небольшие накладные расходы на десериализацию, но это было бы в случае использования Predicate, и работа таким образом сохраняет всю обработку, выполняемую Mapper в JVM, содержащую отображаемые данные, что позволяет избежать любых дорогостоящих процессов / сетевых переходов - в основном это ' должен быть быстрее и определенно сократит сетевой трафик, вызванный межузловой связью.

person Nick Holt    schedule 10.07.2014
comment
Глядя на способы минимизировать накладные расходы на десериализацию, я замечаю, что вы можете получить PortableReader из SerializationService, что позволит вырвать CustomerKey из сериализованного OrderKey без десериализации всего. - person Nick Holt; 10.07.2014
comment
Насколько я знаю, версия NodeAware 3.3+, поэтому, вероятно, это не будет работать в 3.2, и вы используете частный API, который всегда может быть изменен. Мы пока не раскрываем эти вещи пользователям SPI, но вы правы, нам в конечном итоге нужно сделать это, чтобы было удобно использовать. Не могли бы вы заполнить запрос на улучшение на github? - person noctarius; 14.07.2014