Use per-table merge lock in Manager instead of single lock#6378
Use per-table merge lock in Manager instead of single lock#6378dlmarion wants to merge 4 commits into
Conversation
This change introduces a per-table merge lock instead of a single lock to protect reading/writing to ZooKeeper. Closes apache#6374
| Collections.synchronizedMap(new HashMap<>()); | ||
| final Set<TServerInstance> serversToShutdown = Collections.synchronizedSet(new HashSet<>()); | ||
| final Migrations migrations = new Migrations(); | ||
| private final MergeLocks mergeLocks = new MergeLocks(); |
There was a problem hiding this comment.
| private final MergeLocks mergeLocks = new MergeLocks(); | |
| private final LoadingCache<TableId,ReentrantLock> mergeLocks = Caffeine.newBuilder().weakValues() | |
| .scheduler(Scheduler.systemScheduler()).build(k -> new ReentrantLock()); |
My main concern with this is that the TableId in the keys will prevent cleanup of the TableId.cache weak values. The scheduler is supposed to help with cleanup of the keys whose values have been garbage collected, but it may also be a good idea to use String (tableId.canonical()) for the key.
| private final class MergeLocks { | ||
|
|
||
| private final Object lock = new Object(); | ||
| private Map<TableId,ReentrantLock> lockStorage = new HashMap<TableId,ReentrantLock>(); | ||
|
|
||
| private ReentrantLock getLock(TableId tid) { | ||
| synchronized (lock) { | ||
| return lockStorage.computeIfAbsent(tid, k -> new ReentrantLock(true)); | ||
| } | ||
| } | ||
|
|
||
| private void cleanup() { | ||
| synchronized (lock) { | ||
| Set<TableId> removals = new HashSet<>(); | ||
| for (Entry<TableId,ReentrantLock> e : lockStorage.entrySet()) { | ||
| if (!getContext().tableNodeExists(e.getKey()) && !e.getValue().isLocked()) { | ||
| removals.add(e.getKey()); | ||
| } | ||
| } | ||
| removals.forEach(lockStorage::remove); | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
| private final class MergeLocks { | |
| private final Object lock = new Object(); | |
| private Map<TableId,ReentrantLock> lockStorage = new HashMap<TableId,ReentrantLock>(); | |
| private ReentrantLock getLock(TableId tid) { | |
| synchronized (lock) { | |
| return lockStorage.computeIfAbsent(tid, k -> new ReentrantLock(true)); | |
| } | |
| } | |
| private void cleanup() { | |
| synchronized (lock) { | |
| Set<TableId> removals = new HashSet<>(); | |
| for (Entry<TableId,ReentrantLock> e : lockStorage.entrySet()) { | |
| if (!getContext().tableNodeExists(e.getKey()) && !e.getValue().isLocked()) { | |
| removals.add(e.getKey()); | |
| } | |
| } | |
| removals.forEach(lockStorage::remove); | |
| } | |
| } | |
| } |
|
|
||
| public void clearMergeState(TableId tableId) throws KeeperException, InterruptedException { | ||
| synchronized (mergeLock) { | ||
| final ReentrantLock l = mergeLocks.getLock(tableId); |
There was a problem hiding this comment.
| final ReentrantLock l = mergeLocks.getLock(tableId); | |
| final ReentrantLock l = mergeLocks.get(tableId); |
|
|
||
| ThreadPools.watchCriticalScheduledTask( | ||
| context.getScheduledExecutor().scheduleWithFixedDelay(mergeLocks::cleanup, 3, 3, HOURS)); | ||
|
|
There was a problem hiding this comment.
No need for this cleanup code with the weakValues cache's scheduler.
ddanielr
left a comment
There was a problem hiding this comment.
Looks good to me.
I added a comment about some code that looks odd to me, but on a closer look it seems fine.
| synchronized (mergeLock) { | ||
| String path = | ||
| getZooKeeperRoot() + Constants.ZTABLES + "/" + info.getExtent().tableId() + "/merge"; | ||
| final TableId tid = info.getExtent().tableId(); |
There was a problem hiding this comment.
This code looks like we might hit a NPE on the .tableId() call if the extent doesn't exist.
MergeInfo has a no-args constructor that does not set the extent.
Instead the extent is then set via a MergeInfo.readFields() call.
However for all calls of setMergeState the extent or state of MergeInfo is checked so this doesn't seem to be an issue.
This change introduces a per-table merge lock instead of a single lock to protect reading/writing to ZooKeeper.
Closes #6374