Skip to content

Commit 7edd397

Browse files
BB-727: Improve mkdirp to create znode with data
Atomic mkdirp to prevent trigger a watcher for znode creation before data is set on the znode
1 parent 66d8b1a commit 7edd397

2 files changed

Lines changed: 40 additions & 18 deletions

File tree

extensions/notification/configManager/ZookeeperConfigManager.js

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -235,22 +235,12 @@ class ZookeeperConfigManager extends BaseConfigManager {
235235
return async.waterfall([
236236
next => this._checkNodeExists(zkPath, next),
237237
(exists, next) => {
238-
if (!exists) {
239-
return this._createBucketNotifConfigNode(bucket,
240-
err => next(err, exists));
241-
}
242-
return next(null, exists);
243-
},
244-
(exists, next) => this._zkClient.setData(zkPath, Buffer.from(data), -1, err => next(err, exists)),
245-
(exists, next) => {
246-
if (!exists) {
247-
// if znode is created, run getData to set a watcher on the bucket config
248-
// in case another node becomes leader on the raft and modifies the config
249-
// while the current process keeps running
250-
return this._updateLocalStore([bucket], next);
238+
if (exists) {
239+
return this._zkClient.setData(zkPath, Buffer.from(data), -1, next);
240+
} else {
241+
return this._createBucketNotifConfigNode(bucket, data, next);
251242
}
252-
return next();
253-
},
243+
}
254244
], err => {
255245
if (err) {
256246
this.log.error('error saving config', { method, zkPath, data });
@@ -288,7 +278,7 @@ class ZookeeperConfigManager extends BaseConfigManager {
288278
});
289279
}
290280

291-
_createBucketNotifConfigNode(bucket, cb) {
281+
_createBucketNotifConfigNode(bucket, data, cb) {
292282
const method
293283
= 'ZookeeperConfigManager._createBucketNotifConfigNode';
294284
const zkPath = this._getBucketNodeZkPath(bucket);
@@ -297,7 +287,10 @@ class ZookeeperConfigManager extends BaseConfigManager {
297287
bucket,
298288
zkPath,
299289
});
300-
return this._zkClient.mkdirp(zkPath, err => {
290+
// mkdirp to ensure parent path exists,
291+
// then atomically create the znode while setting data immediately
292+
// to avoid other watchers to read the znode because data is set at creation
293+
return this._zkClient.mkdirpWithChildDataOnly(zkPath, Buffer.from(data), err => {
301294
if (err) {
302295
this.log.error('Could not pre-create path in zookeeper', {
303296
method,
@@ -306,7 +299,10 @@ class ZookeeperConfigManager extends BaseConfigManager {
306299
});
307300
return this._callbackHandler(cb, err);
308301
}
309-
return this._callbackHandler(cb);
302+
// if znode is created, run getData to set a watcher on the bucket config
303+
// in case another node becomes leader on the raft and modifies the config
304+
// while the current process keeps running
305+
return this._updateLocalStore([bucket], cb => this._callbackHandler(cb));
310306
});
311307
}
312308

lib/clients/ZookeeperManager.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,32 @@ class ZookeeperManager extends EventEmitter {
319319
return cb();
320320
});
321321
}
322+
323+
/**
324+
* The lib's mkdirp apply data to all the parent znodes in path.
325+
* This helpers uses mkdirp without data for parent znodes, and apply data only to the last node.
326+
*
327+
* @method mkdirpWithChildDataOnly
328+
* @param {String} path - The node path.
329+
* @param {Buffer} [data=undefined] - The data buffer.
330+
* @param {Array} [acls=ACL.OPEN_ACL_UNSAFE] - The array of ACL object.
331+
* @param {CreateMode} [mode=CreateMode.PERSISTENT] - The creation mode.
332+
* @param {Function} callback - The callback function.
333+
* @return {undefined}
334+
*/
335+
mkdirpWithChildDataOnly(path, data, acls, mode, callback) {
336+
// Remove the empty string
337+
const nodes = path.split('/').slice(1);
338+
const lastNode = nodes.pop();
339+
const parentPath = nodes.join('/');
340+
341+
async.waterfall([
342+
next => parentPath ? this.client.mkdirp(parentPath, null, acls, mode, next) : next(),
343+
next => this.client.create(lastNode, data, acls, mode, next),
344+
], callback);
345+
346+
return;
347+
}
322348
}
323349

324350
module.exports = ZookeeperManager;

0 commit comments

Comments
 (0)