尽管没有记录,但具有create_only策略的AeroSpike会引发record_already_exists错误

时间:2018-12-17 10:57:18

标签: java aerospike

在生产环境中,我们使用Aerospike服务器版本3.15.0.3和客户端版本4.0.8。

我们的Aerospike Java客户端在多线程环境中使用CREATE_ONLY策略调用client.put函数。有时,即使没有密钥,我们也会收到KEY_EXISTS_ERROR。

在下面的代码第4行中打印-1(这是密钥)不存在。在LINE 5中,调用CREATE_ONLY的函数将引发错误KEY_ALREADY_EXIST

为什么会这样? AS服务器的CREATE_ONLY政策或Aeropspike Java客户端有问题吗?

在下面添加了代码。

public AerospikeErrorType PutOnce(String key, String nameSpace, String tableName, int expiry, int counter, long reqid) {
    if (key != null) {
        try {
            //LINE 4
            log.info("<" + reqid + "> KY="+key+" BEFORE AERO CALL Get["+get(key,nameSpace,tableName,reqid)+"]");
            //After execute above line Output is:
                   <78465432948> IN AGET KY=DUPMH:g645dhyeu78463sd4 RR:null GET[-1]
                   <78465432948> KY=DUPMH:g645dhyeu78463sd4 BAERO CALL Get[-1]
            //LINE 5
            FirstInsert(key, nameSpace, tableName, expiry, counter);
            //LINE 6
            log.info("<" + reqid + ">  KY="+key+" : "+AerospikeErrorType.RECORD_NOT_EXISTS);
            return AerospikeErrorType.RECORD_NOT_EXISTS;

        } catch (AerospikeException e) {
            if (e.getResultCode() == ResultCode.KEY_EXISTS_ERROR) {
                log.info("<" + reqid + "> KY="+key+" putOnce status2: "+AerospikeErrorType.RECORD_EXISTS +" Error: "+e.getMessage() +" Get["+get(key,nameSpace,tableName,reqid)+"]");
                // This Key is not there but after execute CREATE_ONLY policy, it reached to this catch statement and the Output is:
                       **<78465432948> IN AGET KY=DUPMH:g645dhyeu78463sd4 RR:(gen:1),(exp:283510859),(bins:(:1)) GET[1]
                       <78465432948> KY=DUPMH:g645dhyeu78463sd4 putOnce status2: RECORD_EXISTS Error: Error Code 5: Key already exists Get[1]**
                return AerospikeErrorType.RECORD_EXISTS;
            } else {
                log.info("<" + reqid + "> Error in aerospike operation", e);
                e.printStackTrace();
                log.info("<" + reqid + ">  KY="+key+" putOnce status3: "+AerospikeErrorType.UNKNOWN_STATUS);
                return AerospikeErrorType.UNKNOWN_STATUS;
            }

        } catch (Exception ex) {
            ex.printStackTrace();
            log.info("<" + reqid + ">  KY="+key+" putOnce status4: "+AerospikeErrorType.UNKNOWN_STATUS);
            log.info("<" + reqid + "> Error in aerospike operation", ex);
            return AerospikeErrorType.UNKNOWN_STATUS;
        }
    }
    log.info("<" + reqid + ">  KY="+key+" putOnce status5: "+AerospikeErrorType.UNKNOWN_STATUS);
    return AerospikeErrorType.UNKNOWN_STATUS;
}`


public void FirstInsert(String key, String nameSpace, String tableName, int expiry, int counter) {
    if (key != null) {
        Bin bin = null;
        Key asKey = null;

        WritePolicy WRPOLICY = new WritePolicy();
        WRPOLICY.recordExistsAction = RecordExistsAction.CREATE_ONLY;
        WRPOLICY.expiration = expiry;
        asKey = new Key(nameSpace, tableName, key);
        bin = new Bin(null, counter);
        client.put(WRPOLICY, asKey, bin);
    }
}

================================================ ========================== 这是我的AS Java客户端,用作单例模式。

  /*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.abcd.delivery.helper;

import com.aerospike.client.AerospikeClient;
import com.aerospike.client.AerospikeException;
import com.aerospike.client.Bin;
import com.aerospike.client.Host;
import com.aerospike.client.Key;
import com.aerospike.client.Operation;
import com.aerospike.client.Record;
import com.aerospike.client.ResultCode;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.ClientPolicy;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.RecordExistsAction;
import com.aerospike.client.policy.WritePolicy;
import com.abc.delivery.enums.AerospikeErrorType;
import com.abc.delivery.util.Constants;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;


public class AerospikeDMClient {

    AerospikeClient client = null;
    public static ClientPolicy clientPolicy = new ClientPolicy();
    public BatchPolicy RPOLICY = null;
    public WritePolicy WPOLICY = null;
    public WritePolicy WFEQPOLICY = null;
    private static Logger log = Logger.getLogger(AerospikeDMClient.class);

    private AerospikeDMClient(String aeroSpikeHost, String aeroSpikePort) {

        clientPolicy.maxConnsPerNode = Constants.AEROSPIKE_MAX_CONNECTION;
        clientPolicy.maxSocketIdle = Constants.AEROSPIKE_SOCKET_IDLE_CONNECTION;
        clientPolicy.timeout = Constants.AEROSPIKE_CONNECTION_TIMEDOUT;

        String hostsStr[] = aeroSpikeHost.split(",");
        String portStr[] = aeroSpikePort.split(",");
        Host[] hosts = new Host[hostsStr.length];
        for (int i = 0; i < hosts.length; i++) {
            hosts[i] = new Host(hostsStr[i], Integer.parseInt(portStr[i]));
        }

        client = new AerospikeClient(clientPolicy, hosts);
        RPOLICY = new BatchPolicy();
        WPOLICY = new WritePolicy();
        WFEQPOLICY = new WritePolicy();
        WPOLICY.expiration = 30 * 60;

    }

    public AerospikeClient getClient() {
        return client;
    }

    public ClientPolicy getPolicy() {
        return clientPolicy;
    }

    public List<String> batchGet(List<String> duplist, String nameSpace, String tableName) {
        List<String> aerospikeReturnList = new ArrayList();
        if (duplist != null) {
            try {
                RPOLICY = new BatchPolicy();
                Key[] aeroKey = new Key[duplist.size()];
                for (int i = 0; i < duplist.size(); i++) {
                    aeroKey[i] = new Key(nameSpace, tableName, duplist.get(i));

                }
                Record[] aerospikeList = client.get(RPOLICY, aeroKey);

                if (aerospikeList != null) {

                    for (int i = 0; i < aerospikeList.length; i++) {

                        if (aerospikeList[i] != null) {

                            //log.info("==???==" + aerospikeList[i].getValue("val") + " ===" +aeroKey[i].userKey  + " ===> "+aerospikeList[i].expiration + " ====" + aerospikeList[i].generation);
                            aerospikeReturnList.add((String) aerospikeList[i].getValue("val"));
                        } else {
                            aerospikeReturnList.add(null);
                        }

                    }
                }
            } catch (Exception ex) {
                log.info("Error in aerospike batchget ", ex);
            }

        }
        return aerospikeReturnList;
    }

    public long get(String md5Key, String nameSpace, String tableName, long reqid) {
        long result = -1;
         Record r = null;
        try {
            Policy p = new Policy();
            Key key1 = new Key(nameSpace, tableName, md5Key);
            r = client.get(null, key1);
            if (r != null) {
                result = (Long) r.getValue("");

            }
        } catch (AerospikeException ex) {
            log.info("AerospikeException in get: " ,ex);
        }catch (Exception ex) {
            log.info("Exception in get: " ,ex);
        }
        log.info("<" + reqid + "> IN AGET KY="+md5Key +" RR:"+r + " GET["+result+"]");
        return result;
    }

    public List<String> batchGetWithIndex(List<String> duplist, String nameSpace, String tableName) {
        List<String> aerospikeReturnList = new ArrayList();
        if (duplist != null) {
            RPOLICY = new BatchPolicy();
            Key[] aeroKey = new Key[duplist.size()];
            for (int i = 0; i < duplist.size(); i++) {
                aeroKey[i] = new Key(nameSpace, tableName, duplist.get(i));

            }
            Record[] aerospikeList = client.get(RPOLICY, aeroKey);

            if (aerospikeList != null) {

                for (int i = 0; i < aerospikeList.length; i++) {

                    if (aerospikeList[i] != null) {

                        aerospikeReturnList.add(String.valueOf(i));
                    } else {
                        aerospikeReturnList.add(null);
                    }

                }
            }
            for (int i = 0; i < duplist.size(); i++) {
                log.info("batchGet key: " + duplist.get(i));
            }
        }
        return aerospikeReturnList;
    }

    public void put(List<String> tmpList, String nameSpace, String tableName) {
        if (tmpList != null) {
            try {
                WPOLICY = new WritePolicy();
                WPOLICY.expiration = 30 * 60;
                for (int i = 0; i < tmpList.size(); i++) {
                    Key key = new Key(nameSpace, tableName, tmpList.get(i));
                    Bin bin = new Bin("val", tmpList.get(i));
                    client.put(WPOLICY, key, bin);

                }
            } catch (Exception ex) {
                log.info("Error in aerospike put", ex);
            }
        }
    }

    public void mapPut(Map<String, Integer> ruleExpiryMap, String nameSpace, String tableName) {
        if (ruleExpiryMap != null) {
            try {
                for (Map.Entry<String, Integer> entry : ruleExpiryMap.entrySet()) {
                    WFEQPOLICY.expiration = entry.getValue();
                    Key key = new Key(nameSpace, tableName, entry.getKey());
                    Bin bin = new Bin("val", entry.getKey());
                    client.put(WFEQPOLICY, key, bin);
                }
            } catch (Exception ex) {
                log.info("Error in aerospike mapPut", ex);
            }
        }
    }

    public void freqMapPut(Map<FrquencyCapperHelper, Integer> ruleExpiryMap, String nameSpace, String tableName) {
        try {
            if (ruleExpiryMap != null) {

                for (Map.Entry<FrquencyCapperHelper, Integer> entry : ruleExpiryMap.entrySet()) {
                    try {
                        if (entry.getValue() == 1) {
                            int exp = entry.getKey().getValidity() * 60;
                            WFEQPOLICY.recordExistsAction = RecordExistsAction.CREATE_ONLY;
                            WFEQPOLICY.expiration = Math.abs(exp);
                        } else {
                            WFEQPOLICY.expiration = -2;
                            WFEQPOLICY.recordExistsAction = RecordExistsAction.UPDATE_ONLY;
                        }

                        Key key = new Key(nameSpace, tableName, entry.getKey().getKey());
                        Bin bin = new Bin("val", String.valueOf((Integer) entry.getValue()));
                        client.put(WFEQPOLICY, key, bin);
                    } catch (AerospikeException ex) {
                        log.info("Error in aerospike feqMapPut: " + ex);
                        if (ex.getResultCode() == ResultCode.KEY_EXISTS_ERROR) {
                            WFEQPOLICY.expiration = -2;
                            WFEQPOLICY.recordExistsAction = RecordExistsAction.UPDATE_ONLY;
                            Key key = new Key(nameSpace, tableName, entry.getKey().getKey());
                            Bin bin = new Bin("val", String.valueOf((Integer) entry.getValue()));
                            client.put(WFEQPOLICY, key, bin);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public AerospikeErrorType PutOnce(String key, String nameSpace, String tableName, int expiry, int counter, long reqid) {
        if (key != null) {
            try {
                log.info("<" + reqid + "> KY="+key+"BAERO CALL Get["+get(key,nameSpace,tableName,reqid)+"]");
                FirstInsert(key, nameSpace, tableName, expiry, counter);
                log.info("<" + reqid + ">  KY="+key+" putOnce status1: "+AerospikeErrorType.RECORD_NOT_EXISTS);
                return AerospikeErrorType.RECORD_NOT_EXISTS;

            } catch (AerospikeException e) {
                if (e.getResultCode() == ResultCode.KEY_EXISTS_ERROR) {
                    log.info("<" + reqid + "> KY="+key+" putOnce status2: "+AerospikeErrorType.RECORD_EXISTS +" Error: "+e.getMessage() +" Get["+get(key,nameSpace,tableName,reqid)+"]");
                    return AerospikeErrorType.RECORD_EXISTS;
                } else {
                    log.info("<" + reqid + "> Error in aerospike operation", e);
                    e.printStackTrace();
                    log.info("<" + reqid + ">  KY="+key+" putOnce status3: "+AerospikeErrorType.UNKNOWN_STATUS);
                    return AerospikeErrorType.UNKNOWN_STATUS;
                }

            } catch (Exception ex) {
                ex.printStackTrace();
                log.info("<" + reqid + ">  KY="+key+" putOnce status4: "+AerospikeErrorType.UNKNOWN_STATUS);
                log.info("<" + reqid + "> Error in aerospike operation", ex);
                return AerospikeErrorType.UNKNOWN_STATUS;
            }
        }
        log.info("<" + reqid + ">  KY="+key+" putOnce status5: "+AerospikeErrorType.UNKNOWN_STATUS);
        return AerospikeErrorType.UNKNOWN_STATUS;
    }

    public void FirstInsert(String key, String nameSpace, String tableName, int expiry, int counter) {
        if (key != null) {
            Bin bin = null;
            Key asKey = null;

            WritePolicy WRPOLICY = new WritePolicy();
            WRPOLICY.recordExistsAction = RecordExistsAction.CREATE_ONLY;
            WRPOLICY.expiration = expiry;
            asKey = new Key(nameSpace, tableName, key);
            bin = new Bin(null, counter);
            client.put(WRPOLICY, asKey, bin);
        }
    }

    public Integer IncrAndGet(String key, String nameSpace, String tableName, int expiry, int counter, long reqid) {
        if (key != null) {
            Bin bin = null;
            Key asKey = null;
            try {
                FirstInsert(key, nameSpace, tableName, expiry, counter);
                return 1;

            } catch (AerospikeException e) {
                if (e.getResultCode() == ResultCode.KEY_EXISTS_ERROR) {
                    WFEQPOLICY.expiration = -2;
                    WFEQPOLICY.recordExistsAction = RecordExistsAction.UPDATE_ONLY;
                    asKey = new Key(nameSpace, tableName, key);
                    bin = new Bin(null, counter);
                    try {
                        return client.operate(WFEQPOLICY, asKey, Operation.add(bin), Operation.get()).getInt("");
                    } catch (AerospikeException e1) {
                        if (e1.getResultCode() == ResultCode.KEY_NOT_FOUND_ERROR) {
                            FirstInsert(key, nameSpace, tableName, expiry, counter);
                            return 1;
                        }
                    }
                } else {
                    log.info("<" + reqid + "> Error in aerospike operation", e);
                    e.printStackTrace();
                    return 0;
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                log.info("<" + reqid + "> Error in aerospike operation", ex);
                return 0;
            }
        }
        return 0;
    }

}

1 个答案:

答案 0 :(得分:1)

看起来好像您有一个单例(构造函数是私有的),但是我没有看到一个工厂方法返回该假定的单个对象,因此不确定发生了什么。也许只是缺少代码。

Java客户端是多线程的。可以在单个客户端的不同线程上向服务器发送多个操作。在服务器节点上,每个核心通常都有一个工作队列(transaction queue),传入的请求将在每个队列中分配。不能保证命令会按照命令的顺序执行。因此,如果您的客户端在一个线程上发送get(k1)并在另一个线程上发送put-create-only(k1),并先以get方式启动,然后以put方式启动,则它们可能仍然以相反的顺序执行。队列之间不协调,它们在指定的操作上尽力而为。

此外,多个客户可以为您在概念上认为是单个线性过程的事物生成不同的时序。没有理由认为在大量操作中,客户端c1的c1.get(k1)和c1.create(k1)之间不会发生创建。

基本上,create(k1)之前的get(k1)不能保证任何事情。取而代之的是,您可以只先使用put-with-create-only(k1),然后处理该异常并将其重复为put-with-update-only(k1)。

特别是对于频次上限用例,在这种情况下,您希望第一个操作设置绝对TTL,随后的更新不更改到期时间(使用-2的到期时间),则可以使用其他方法。您可以创建频率上限名称空间使用的default-ttl,该名称空间具有正确的绝对TTL。在客户端中,您始终使用带有到期时间-2的put()操作。现在,这是一种upsert行为,并且首次写入将插入并继承名称空间的默认TTL,而后续写入将在不修改TTL的情况下进行更新。

在您的代码中,我看不到任何其他原因将Aerospike客户端包装到您自己的方法中(get包装get,firstInsert / update包装put操作)。我建议您直接使用Aerospike客户端,以减少阅读和调试代码的难度。我为您要实现的目标提供了两种不同的方法。就个人而言,我喜欢第二种,其中客户端发送的默认RecordExistsAction为UPDATE的-2(意味着创建或替换,又名Upsert)。处理异常的速度很慢,并且您通常会采用该代码路径,因为记录仅创建一次,但可能会多次更新。在第三种方法中,您首先尝试使用UPDATE_ONLY,捕获异常,然后尝试使用CREATE_ONLY进行第二次尝试,但是随后您必须捕获一个异常,然后重复UPDATE_ONLY。这样您就可以了解第二种方法为什么更清洁的原因。