Самый эффективный способ создания пути в zookeeper, где корневые элементы пути могут или не могут существовать?

Представьте путь "/root/child1/child2/child3"

Представьте себе в zookeeper, что, возможно, часть этого существует, скажем: "/root/child1"

В zookeeper нет эквивалента "mkdir -p"; Кроме того, ZooKeeper.multi() не удастся, если какая-либо операция завершится неудачей, поэтому "make path" не может быть переработана в многопользовательском вызове. Кроме того, у вас может быть другой клиент, пытающийся сделать один и тот же путь...

Это то, что я придумал для создания пути. Интересно, стоит ли даже проверять, существует ли какая-либо часть или нет, чтобы сохранить обратный вызов вызова exists().

//String[] pathParts new String[] { "root", "child1", "child2", "child3" };

public void savePath(String[] pathParts) {
    if (zooKeeper.exists(pathString, false) != null) return;
    StringBuilder path = new StringBuilder();
    for (String pathElement : pathParts) {
        path.append(UNIX_FILE_SEPARATOR).append(pathElement);
        String pathString = path.toString();
        try {
            //bother with the exists call or not?
            if (zooKeeper.exists(pathString, false) == null) {
                zooKeeper.create(pathString, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
        } catch (KeeperException e) {
            if (e.code() != KeeperException.Code.NODEEXISTS)
                throw e;
        }
    }
}

Каким будет самый эффективный способ сделать это? Предполагая, что a) вы заранее не знаете, какая часть пути уже существует, и б) какой-то другой клиент может попытаться написать тот же путь (и мы хотим избежать блокировки).

Ответ 1

Существующий вызов может быть выполнен с 1 обращением в оба конца от сервера к клиенту.

Созданный вызов имеет одно и то же путешествие в оба конца, но создание - это операция записи, которая влечет за собой еще пару раундов между серверами в кластере zk, поэтому создание немного дороже, чем существует.

Итак, общее время для вашего алгоритма:

Время на 1 чтение op * Вероятность node уже существует + (Время для 1 записи op) * (1 - Вероятность node уже существует).

Таким образом, любой из if(!exist()) create() vs create() может быть быстрее. В конце концов, это, вероятно, не имеет значения.

Если вы хотите быть очень быстрым, вы можете использовать async api, чтобы вы могли создавать все компоненты своего пути, не дожидаясь ответа сервера на запросы 1 на 1.

final AtomicBoolean success = new AtomicBoolean(false);
final CountdownLatch latch = new CountdownLatch(1);
StringCallback cb = new StringCallback() {
    processResult(int rc, String path, Object ctx, String name) {
        if(name.equals(pathString ) {
            //wait for the last path
            success.set(rc == KeeperException.Code.NODEEXISTS ||
                        rc == KeeperException.Code.OK);
            latch.countDown();
        }
    }
};

StringBuilder path = new StringBuilder();
for (String pathElement : pathParts) {
    path.append(UNIX_FILE_SEPARATOR).append(pathElement);
    String pathString = path.toString();
    //send requests to create all parts of the path without waiting for the
    //results of previous calls to return
    zooKeeper.create(pathString, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT, cb);
}
latch.await();
if(!success.get()) { 
    throw ...
}

Ответ 2

Вы можете использовать библиотеку Netflix curator, которая значительно упрощает использование zookeeper

client.create().withMode(CreateMode.PERSISTENT).forPath("/root/child1/child2/child3", new byte[0]).withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE).creatingParentsIfNeeded();