Обновление отдельной колонки в Pivotal GemFire

Насколько мне известно, нет возможности обновлять отдельные столбцы с помощью запроса в gemfire. Чтобы обновить отдельный столбец, я в настоящее время получаю весь старый объект, изменяю измененное значение и сохраняю его. Если кто что-то реализовал по обновлению отдельных столбцов, поделитесь пожалуйста.

@Region("tracking")
public class Tracking implements Serializable {
public String id;
public String status;
public String program;
}



@Region("tracking")
public interface TrackingQueryRepository extends CrudRepository<Tracking, String> {
}

Я новичок в реализации Delta Propagation. Я прочитал руководство пользователя, попытался реализовать и получил исключение, указанное ниже. Не могли бы вы поделиться своими мыслями по этому поводу.

Другой.java - класс домена

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.Serializable;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.mapping.Region;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gemstone.gemfire.Delta;
import com.gemstone.gemfire.InvalidDeltaException;


@Region("delta")
public class Another implements Delta, Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    private String anotherId;

    @JsonProperty("anotherProgramId")
    private String anotherProgramId;

    public Another() {
    }

    public Another(String anotherId, String anotherProgramId) {
        this.anotherId = anotherId;
        this.anotherProgramId = anotherProgramId;
    }

    public String getAnotherId() {
        return anotherId;
    }

    public void setAnotherId(String anotherId) {
        this.anotherIdChd = true;
        this.anotherId = anotherId;
    }

    public String getAnotherProgramId() {
        return anotherProgramId;
    }

    public void setAnotherProgramId(String anotherProgramId) {
        this.anotherProgramIdChd = true;
        this.anotherProgramId = anotherProgramId;
    }

    private transient boolean anotherIdChd = false;
    private transient boolean anotherProgramIdChd = false;

    @Override
    public String toString() {
        return "Another [anotherId=" + anotherId + ", anotherProgramId=" + anotherProgramId + "]";
    }

    @Override
    public void fromDelta(DataInput in) throws IOException, InvalidDeltaException {

        if (in.readBoolean()) {
            // Read the change and apply it to the object
            this.anotherId = in.toString();
            System.out.println(" Applied delta to field 'anotherId' = " + this.anotherId);
        }
        if (in.readBoolean()) {
            this.anotherProgramId = in.toString();
            System.out.println(" Applied delta to field 'anotherProgramId' = " + this.anotherProgramId);
        }
    }

    @Override
    public boolean hasDelta() {
        return this.anotherIdChd || this.anotherProgramIdChd;

    }

    @Override
    public void toDelta(DataOutput out) throws IOException {
        System.out.println("Extracting delta from " + this.toString());
        out.writeBoolean(anotherIdChd);
        if (anotherIdChd) {
            // Write just the changes into the data stream

            out.writeUTF(this.anotherId);
            // Once the delta information is written, reset the delta status
            // field
            this.anotherIdChd = false;
            System.out.println(" Extracted delta from field 'anotherId' = " + this.anotherId);
        }
        out.writeBoolean(anotherProgramIdChd);
        if (anotherProgramIdChd) {
            out.writeUTF(this.anotherProgramId);
            this.anotherProgramIdChd = false;
            System.out.println(" Extracted delta from field 'anotherProgramId' = " + this.anotherProgramId);
        }

    }

}

Client-cache.xml

<pdx>
        <pdx-serializer>
            <class-name>com.gemstone.gemfire.pdx.ReflectionBasedAutoSerializer</class-name>
            <parameter name="classes">
                <string>com\.rs\.main\..+</string>
            </parameter>
        </pdx-serializer>
    </pdx>

Пространство имен Spring XML

<util:properties id="gemfire-props">
<prop key="delta-propagation">true</prop>
</util:properties>
<gfe:client-cache pool-name="serverPool" cache-xml-location="classpath:client-cache.xml" properties-ref="gemfire-props"/>
<gfe:client-region id="delta" pool-name="serverPool" shortcut="PROXY" cloning-enabled="true">

Версия локального экземпляра gemfire - pivotal-gemfire-9.0.1

Создание региона Создать регион –name = delta –type = REPLICATE

Исключение:

2017-05-08 22:17:12.370 ERROR 14696 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: remote server on 10.148.210.249(:loner):53784:e10627eb: com.gemstone.gemfire.pdx.PdxSerializationException: Could not create an instance of a class com.rs.main.Another; nested exception is com.gemstone.gemfire.cache.client.ServerOperationException: remote server on 10.148.210.249(:loner):53784:e10627eb: com.gemstone.gemfire.pdx.PdxSerializationException: Could not create an instance of a class com.rs.main.Another] with root cause

java.lang.ClassNotFoundException: com.rs.main.Another
    at org.apache.geode.internal.ClassPathLoader.forName(ClassPathLoader.java:437) ~[na:na]
    at org.apache.geode.internal.InternalDataSerializer.getCachedClass(InternalDataSerializer.java:4010) ~[na:na]
    at org.apache.geode.pdx.internal.PdxType.getPdxClass(PdxType.java:235) ~[na:na]
    at org.apache.geode.pdx.internal.PdxReaderImpl.basicGetObject(PdxReaderImpl.java:687) ~[na:na]
    at org.apache.geode.pdx.internal.PdxReaderImpl.getObject(PdxReaderImpl.java:682) ~[na:na]
    at org.apache.geode.internal.InternalDataSerializer.readPdxSerializable(InternalDataSerializer.java:3218) ~[na:na]
    at org.apache.geode.internal.InternalDataSerializer.basicReadObject(InternalDataSerializer.java:3005) ~[na:na]
    at org.apache.geode.DataSerializer.readObject(DataSerializer.java:2897) ~[na:na]
    at org.apache.geode.internal.util.BlobHelper.deserializeBlob(BlobHelper.java:90) ~[na:na]
    at org.apache.geode.internal.cache.EntryEventImpl.deserialize(EntryEventImpl.java:1891) ~[na:na]
    at org.apache.geode.internal.cache.EntryEventImpl.deserialize(EntryEventImpl.java:1884) ~[na:na]
    at org.apache.geode.internal.cache.VMCachedDeserializable.getDeserializedValue(VMCachedDeserializable.java:134) ~[na:na]
    at org.apache.geode.internal.cache.EntryEventImpl.processDeltaBytes(EntryEventImpl.java:1687) ~[na:na]
    at org.apache.geode.internal.cache.EntryEventImpl.setNewValueInRegion(EntryEventImpl.java:1558) ~[na:na]
    at org.apache.geode.internal.cache.EntryEventImpl.putExistingEntry(EntryEventImpl.java:1504) ~[na:na]
    at org.apache.geode.internal.cache.AbstractRegionMap.updateEntry(AbstractRegionMap.java:2959) ~[na:na]
    at org.apache.geode.internal.cache.AbstractRegionMap.basicPut(AbstractRegionMap.java:2782) ~[na:na]
    at org.apache.geode.internal.cache.LocalRegion.virtualPut(LocalRegion.java:5750) ~[na:na]
    at org.apache.geode.internal.cache.DistributedRegion.virtualPut(DistributedRegion.java:337) ~[na:na]
    at org.apache.geode.internal.cache.LocalRegionDataView.putEntry(LocalRegionDataView.java:151) ~[na:na]
    at org.apache.geode.internal.cache.LocalRegion.basicUpdate(LocalRegion.java:5730) ~[na:na]
    at org.apache.geode.internal.cache.LocalRegion.basicBridgePut(LocalRegion.java:5374) ~[na:na]
    at org.apache.geode.internal.cache.tier.sockets.command.Put65.cmdExecute(Put65.java:381) ~[na:na]
    at org.apache.geode.internal.cache.tier.sockets.BaseCommand.execute(BaseCommand.java:141) ~[na:na]
    at org.apache.geode.internal.cache.tier.sockets.ServerConnection.doNormalMsg(ServerConnection.java:776) ~[na:na]
    at org.apache.geode.internal.cache.tier.sockets.ServerConnection.doOneMessage(ServerConnection.java:904) ~[na:na]
    at org.apache.geode.internal.cache.tier.sockets.ServerConnection.run(ServerConnection.java:1160) ~[na:na]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) ~[na:1.8.0_121]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) ~[na:1.8.0_121]
    at org.apache.geode.internal.cache.tier.sockets.AcceptorImpl$1$1.run(AcceptorImpl.java:519) ~[na:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]

person Vigneshwaran    schedule 08.05.2017    source источник


Ответы (3)


Привет (снова) Vigneshwaran-

Верно, поэтому возможности запросов GemFire ​​(через QueryService) предназначен исключительно для выполнения запросов. (т.е. операторы SELECT). В GemFire ​​нет эквивалента OQL для ОБНОВЛЕНИЙ и УДАЛЕНИЙ. GemFire ​​- это хранилище ключей / значений с Map-подобными операциями (например, get(key), put(key, value) и т. Д.), Где вы обычно работаете со всем объектом домена приложения. Однако есть несколько функций GemFire, которые могут помочь вам независимо от того, является ли ваше приложение одноранговым кешем (то есть членом кластера) или клиентом кеширования. Обычно приложения являются клиентами кеширования и имеют / используют ClientCache, где кластер является автономным, а клиенты подключаются к кластеру так же, как СУБД.

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

Как упоминает Уэс выше, очень типично использовать область PARTITION, особенно для «транзакционных» данных (ПРИМЕЧАНИЕ: области REPLICATE больше подходят для справочных данных, которые меняются нечасто).

Там, где вам может помочь «Функция», вы должны написать код Функция для обновления объекта домена приложения. «Обновление» может быть передано в «аргументах» функции. Чтобы вызвать функцию, вы используете GemFire ​​FunctionService, чтобы получить Исполнение одним из целевых методов (например, [onRegion("tracking")][7]).

ПРИМЕЧАНИЕ: другие методы нацеливания (а именно, onMember(s) и onServer(s)) зависят от того, является ли ваше приложение «одноранговым» или «клиентским» соответственно. Например, вы не можете вызвать onMember(s), если ваше приложение является клиентским, поскольку оно предполагает, что ваше приложение является «одноранговым». Точно так же вы не можете вызвать onServer(s), если ваше приложение является партнером, поскольку оно предполагает, что ваше приложение является «клиентом». onRegion(..) работает независимо от того, является ли приложение одноранговым или клиентским. Хотя вы можете подумать, почему бы не использовать onRegion все время, существуют технические преимущества использования других форм таргетинга в зависимости от вашего UC (например, подумайте о группах серверов и маршрутизации). В любом случае...

Когда регион является РАЗДЕЛОМ, вы также можете установить [optimizeForWrite()][8] функции, что означает, что функция будет обновлять данные региона и, следовательно, будет перенаправлена ​​в первичный сегмент РАЗДЕЛА для ключа, если ключ указан с использованием фильтрации, как описано выше Уэсом.

Согласованность региона PARTITION происходит из того факта, что все обновления маршрутизируются и записываются в первую очередь на «первичный» (независимо от того, какой сервер получает обновление клиента, что может быть сервером, который даже не размещает регион или данные / ключ, о которых идет речь; т.е. другой шард). После обновления первичного сервера изменения данных распространяются (распределяются) на другие узлы в кластере, на котором размещены вторичные серверы для раздела / набора сегментированных данных. Это «транзакционная» согласованность, о которой Уэс упоминает выше.

ПРИМЕЧАНИЕ. РАЗДЕЛЕНИЕ - это просто другое слово для РАЗДЕЛЕНИЯ данных, при котором данные равномерно распределяются по кластеру доступных узлов. Когда узлы добавляются / удаляются, данные перебалансируются. РАЗДЕЛ также может иметь избыточность. Они называются вторичными. РАЗДЕЛЕНИЕ Регионы помогают уменьшить задержку и пропускную способность, поскольку данные разделены (по умолчанию на 113 сегментов), где каждая корзина имеет первичную и, возможно, одну или несколько копий (вторичные для избыточности; HA), тем самым улучшая пропускную способность как чтения, так и записи.

Кроме того, если данные должны сохраняться, вы также можете установить функцию HA. Это позволит повторять попытки в случае неудач.

Однако, несмотря на все эти преимущества, вам все равно придется иметь дело с практическими рекомендациями по обновлению объекта домена вашего приложения в функции на сервере. Вы также должны иметь дело с «отображением», поскольку на самом деле нет эквивалента ORM в хранилищах ключей / значений, таких как GemFire. Конечно, это не так уж и сложно, но, возможно, есть способ получше.

Есть еще одна функция, которая называется Delta Propagation. По сути, вы всегда получаете и обновляете полную версию GemFire ​​всякий раз, когда делаете обновления.

ПРИМЕЧАНИЕ: можно запрашивать поля выбора объекта в виде проекции, подобной моде, но это не является прокси-сервером и никак не связано с фактическим объектом.

Воспользовавшись возможностями GemFire ​​сериализации, вы можете использовать дельта-распространение.

При реализации «дельт» на самом деле сериализуется, пересылается по сети только разница в объекте домена приложения, будь то между клиентом и сервером или между одноранговыми узлами при поддержании политик избыточности. Для вас это совершенно незаметно. Вы получаете свой объект (на стороне клиента), обновляете его и помещаете. GemFire ​​обрабатывает логику отправки «дельты» за вас.

Кроме того, при использовании топологии клиент / сервер и регионов PARTITION на серверах в кластере вы можете включить однопроходный доступ, который эффективно направляет данные на сервер, содержащий «первичный» сегмент, тем самым избегая дополнительных сетевых переходов, которые повлияют на вашу воспринимаемую задержку для каждой операции.

Таким образом, между Deltas и Single-Hop вы получаете довольно производительное решение и по-прежнему можете использовать объектно-ориентированный подход, используя API-интерфейсы объектов домена вашего приложения, как и следовало ожидать.

Однако помните о подводных камнях при использовании Deltas.

В любом случае, пища для размышлений. Обычно у вас всегда есть более одного способа выполнить задачу, но лучший подход не всегда очевиден, пока вы не измеряете и не оцениваете желаемый эффект на основе вашего UC / цели.

Привет, Джон

person John Blum    schedule 08.05.2017
comment
Привет, Джон! Это приложение представляет собой приложение Cache-Client и уже использует возможности сериализации Gemfire. Мы решили перейти на дельта-распространение. Ваши объяснения точны и очень полезны. Я хотел бы реализовать и тогда принимаю ваш ответ. Большое тебе спасибо. Ваша помощь была для меня неоценима. - person Vigneshwaran; 09.05.2017
comment
Я пробовал использовать Delta Propagation, и мне это не подошло из-за PDX. Пробуем реализовать с помощью Function onRegion (). WithFilter (key) .withArgs (columnsAndValuesMap). Не могли бы вы поделиться фрагментами кода, которые будут написаны для функции сервера (метод Execute). заранее спасибо - person Vigneshwaran; 14.06.2017

Вы не можете обновить столбец с помощью службы Query. Я рекомендую вам рассмотреть услугу Function для обеспечения согласованности транзакций. Сделайте область разделенной и вызовите функцию, используя .onRegion (). WithFilter (ключ) .withArgs (columnsAndValuesMap).

Ваша функция прочитает объект, применит обновления и поместит.

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

person Wes Williams    schedule 08.05.2017
comment
Уэс - Не могли бы вы поделиться фрагментом кода функции для обработки этого сценария на сайте function. Ценю вашу помощь. - person Vigneshwaran; 20.06.2017

Альтернативный способ достижения той же цели - использование настраиваемой функции, которая обновляет значение (BeanUtils) после выполнения распределенной блокировки.

Это может увеличить производительность, но гарантирует целостность данных. Это компромисс.

См. Ниже псевдокод

 try{

 //this can be regionName 
 dls = DistributedLockService.getServiceNamed(arbitrary-lock-name) 
 //the key is normally the object @Id
 dls.lock(some-key, waitTimeOut, leaseTimeOut)

 row = region.get(id)
 //Here we copy the desired value (input to function) to the latest value
 BeanUtils.copyProperty(row, key, value);      
 //Insert the modified record to Gemfire - now this becomes equivalent of update <region> set value =  for a specific property.
 region.put(id, row) 

 } finally{

 dls.unlock(some-key);

 }
person Jebuselwyn Martin    schedule 25.01.2018