Merge "Fixes for IT base"
[genius.git] / idmanager / idmanager-impl / src / main / java / org / opendaylight / genius / idmanager / IdManager.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.genius.idmanager;
10
11 import java.util.ArrayList;
12 import java.util.Collections;
13 import java.util.Comparator;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.NoSuchElementException;
17 import java.util.Timer;
18 import java.util.TimerTask;
19 import java.util.concurrent.ConcurrentMap;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import java.util.stream.Collectors;
23
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
28 import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
29 import org.opendaylight.genius.idmanager.ReleasedIdHolder.DelayedIdEntry;
30 import org.opendaylight.genius.idmanager.jobs.CleanUpJob;
31 import org.opendaylight.genius.idmanager.jobs.IdHolderSyncJob;
32 import org.opendaylight.genius.idmanager.jobs.LocalPoolCreateJob;
33 import org.opendaylight.genius.idmanager.jobs.LocalPoolDeleteJob;
34 import org.opendaylight.genius.idmanager.jobs.UpdateIdEntryJob;
35 import org.opendaylight.genius.mdsalutil.MDSALUtil;
36 import org.opendaylight.genius.utils.cache.CacheUtil;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdRangeInput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdRangeOutput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdRangeOutputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.DeleteIdPoolInput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdPools;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPool;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.IdPoolKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.AvailableIdsHolder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.AvailableIdsHolderBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ChildPools;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.IdEntries;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ReleasedIdsHolder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.id.pools.id.pool.ReleasedIdsHolderBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.released.ids.DelayedIdEntries;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
59 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
60 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
61 import org.opendaylight.yangtools.yang.common.RpcResult;
62 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 import com.google.common.base.Optional;
67 import com.google.common.util.concurrent.CheckedFuture;
68 import com.google.common.util.concurrent.Futures;
69
70 public class IdManager implements IdManagerService, AutoCloseable {
71     private static final Logger LOG = LoggerFactory.getLogger(IdManager.class);
72     private static final long DEFAULT_IDLE_TIME = 24 * 60 * 60;
73     private final DataBroker broker;
74     private LockManagerService lockManager;
75     private ConcurrentMap<String, IdLocalPool> localPool;
76     private Timer cleanJobTimer = new Timer();
77
78     @Override
79     public void close() throws Exception {
80         LOG.info("IDManager Closed");
81     }
82
83     public IdManager(DataBroker db, LockManagerService lockManager) {
84         broker = db;
85         this.lockManager = lockManager;
86         CacheUtil.createCache(IdUtils.ID_POOL_CACHE);
87         localPool = (ConcurrentMap<String, IdLocalPool>) CacheUtil.getCache(IdUtils.ID_POOL_CACHE);
88         populateCache();
89     }
90
91     private void populateCache() {
92         // If IP changes during reboot, then there will be orphaned child pools.
93         InstanceIdentifier<IdPools> idPoolsInstance = IdUtils.getIdPools();
94         Optional<IdPools> idPoolsOptional= MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, idPoolsInstance);
95         if (!idPoolsOptional.isPresent()) {
96             return;
97         }
98         IdPools idPools = idPoolsOptional.get();
99         List<IdPool> idPoolList = idPools.getIdPool();
100         idPoolList
101                 .parallelStream()
102                 .filter(idPool -> idPool.getParentPoolName() != null
103                         && !idPool.getParentPoolName().isEmpty()
104                         && IdUtils.getLocalPoolName(idPool.getParentPoolName())
105                                 .equals(idPool.getPoolName()))
106                 .forEach(
107                         idPool -> updateLocalIdPoolCache(idPool,
108                                 idPool.getParentPoolName()));
109     }
110
111     public boolean updateLocalIdPoolCache(IdPool idPool, String parentPoolName) {
112         IdLocalPool idLocalPool = new IdLocalPool(idPool.getPoolName());
113         AvailableIdsHolder availableIdsHolder = idPool.getAvailableIdsHolder();
114         AvailableIdHolder availableIdHolder = new AvailableIdHolder(availableIdsHolder.getStart(), availableIdsHolder.getEnd());
115         availableIdHolder.setCur(availableIdsHolder.getCursor());
116         ReleasedIdsHolder releasedIdsHolder = idPool.getReleasedIdsHolder();
117         ReleasedIdHolder releasedIdHolder = new ReleasedIdHolder(releasedIdsHolder.getDelayedTimeSec());
118         releasedIdHolder.setAvailableIdCount(releasedIdsHolder.getAvailableIdCount());
119         List<DelayedIdEntries> delayedEntries = releasedIdsHolder.getDelayedIdEntries();
120         List<DelayedIdEntry> delayedIdEntryInCache = new ArrayList<>();
121         if (delayedEntries != null) {
122             delayedIdEntryInCache = delayedEntries
123                     .parallelStream()
124                     .map(delayedIdEntry -> new DelayedIdEntry(delayedIdEntry
125                             .getId(), delayedIdEntry.getReadyTimeSec()))
126                             .sorted(new Comparator<DelayedIdEntry>() {
127
128                                 @Override
129                                 public int compare(DelayedIdEntry idEntry1,
130                                         DelayedIdEntry idEntry2) {
131                                     return Long.compare(idEntry1.getReadyTimeSec(),
132                                             idEntry2.getReadyTimeSec());
133                                 }
134                             }).collect(Collectors.toList());
135         }
136         releasedIdHolder.setDelayedEntries(delayedIdEntryInCache);
137         idLocalPool.setAvailableIds(availableIdHolder);
138         idLocalPool.setReleasedIds(releasedIdHolder);
139         localPool.put(parentPoolName, idLocalPool);
140         if (LOG.isDebugEnabled()) {
141             LOG.debug("Populating cache for {} with {}", idLocalPool.getPoolName(), idLocalPool);
142         }
143         return true;
144     }
145
146     @Override
147     public Future<RpcResult<Void>> createIdPool(CreateIdPoolInput input) {
148         if (LOG.isDebugEnabled()) {
149             LOG.debug("createIdPool called with input {}", input);
150         }
151         String poolName = input.getPoolName();
152         long low = input.getLow();
153         long high = input.getHigh();
154         long blockSize = IdUtils.computeBlockSize(low, high);
155         RpcResultBuilder<Void> createIdPoolRpcBuilder;
156         IdUtils.lockPool(lockManager, poolName);
157         try {
158             WriteTransaction tx = broker.newWriteOnlyTransaction();
159             poolName = poolName.intern();
160             IdPool idPool;
161             idPool = createGlobalPool(tx, poolName, low, high, blockSize);
162             String localPoolName = IdUtils.getLocalPoolName(poolName);
163             IdLocalPool idLocalPool = localPool.get(poolName);
164             if (idLocalPool == null) {
165                 createLocalPool(tx, localPoolName, idPool);
166                 IdUtils.updateChildPool(tx, idPool.getPoolName(), localPoolName);
167             }
168             submitTransaction(tx);
169             createIdPoolRpcBuilder = RpcResultBuilder.success();
170         } catch (Exception ex) {
171             LOG.error("Creation of Id Pool {} failed due to {}", poolName, ex);
172             createIdPoolRpcBuilder = RpcResultBuilder.failed();
173             createIdPoolRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
174         } finally {
175             IdUtils.unlockPool(lockManager, poolName);
176         }
177         return Futures.immediateFuture(createIdPoolRpcBuilder.build());
178     }
179
180     @Override
181     public Future<RpcResult<AllocateIdOutput>> allocateId(AllocateIdInput input) {
182         if (LOG.isDebugEnabled()) {
183             LOG.debug("AllocateId called with input {}", input);
184         }
185         String idKey = input.getIdKey();
186         String poolName = input.getPoolName();
187         String localPoolName = IdUtils.getLocalPoolName(poolName);
188         RpcResultBuilder<AllocateIdOutput> allocateIdRpcBuilder;
189         long newIdValue = -1;
190         AllocateIdOutputBuilder output = new AllocateIdOutputBuilder();
191         try {
192             //allocateIdFromLocalPool method returns a list of IDs with one element. This element is obtatined by get(0)
193             newIdValue = allocateIdFromLocalPool(poolName, localPoolName, idKey, 1).get(0);
194             output.setIdValue(newIdValue);
195             allocateIdRpcBuilder = RpcResultBuilder.success();
196             allocateIdRpcBuilder.withResult(output.build());
197         } catch (Exception ex) {
198             LOG.error("Allocate id in pool {} failed due to {}", poolName, ex);
199             allocateIdRpcBuilder = RpcResultBuilder.failed();
200             allocateIdRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
201         }
202         return Futures.immediateFuture(allocateIdRpcBuilder.build());
203     }
204
205     @Override
206     public Future<RpcResult<AllocateIdRangeOutput>> allocateIdRange(AllocateIdRangeInput input) {
207         if (LOG.isDebugEnabled()) {
208             LOG.debug("AllocateIdRange called with input {}", input);
209         }
210         String idKey = input.getIdKey();
211         String poolName = input.getPoolName();
212         long size = input.getSize();
213         String localPoolName = IdUtils.getLocalPoolName(poolName);
214         RpcResultBuilder<AllocateIdRangeOutput> allocateIdRangeRpcBuilder;
215         List<Long> newIdValuesList = new ArrayList<>();
216         AllocateIdRangeOutputBuilder output = new AllocateIdRangeOutputBuilder();
217         try {
218             newIdValuesList = allocateIdFromLocalPool(poolName, localPoolName, idKey, size);
219             Collections.sort(newIdValuesList);
220             output.setIdValues(newIdValuesList);
221             allocateIdRangeRpcBuilder = RpcResultBuilder.success();
222             allocateIdRangeRpcBuilder.withResult(output.build());
223         } catch (NullPointerException e){
224             LOG.error("Not enough Ids available in the pool {} for requested size {}", poolName, size);
225             allocateIdRangeRpcBuilder = RpcResultBuilder.failed();
226             allocateIdRangeRpcBuilder.withError(ErrorType.APPLICATION, e.getMessage());
227         } catch (Exception ex) {
228             LOG.error("Allocate id range in pool {} failed due to {}", poolName, ex);
229             allocateIdRangeRpcBuilder = RpcResultBuilder.failed();
230             allocateIdRangeRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
231         }
232         return Futures.immediateFuture(allocateIdRangeRpcBuilder.build());
233     }
234
235     @Override
236     public Future<RpcResult<Void>> deleteIdPool(DeleteIdPoolInput input) {
237         if (LOG.isDebugEnabled()) {
238             LOG.debug("DeleteIdPool called with input {}", input);
239         }
240         String poolName = input.getPoolName();
241         RpcResultBuilder<Void> deleteIdPoolRpcBuilder;
242         try {
243             InstanceIdentifier<IdPool> idPoolToBeDeleted = IdUtils.getIdPoolInstance(poolName);
244             poolName = poolName.intern();
245             synchronized(poolName) {
246                 IdPool idPool = getIdPool(idPoolToBeDeleted);
247                 List<ChildPools> childPoolList = idPool.getChildPools();
248                 if (childPoolList != null) {
249                     childPoolList.parallelStream().forEach(childPool -> deletePool(childPool.getChildPoolName()));
250                 }
251                 MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, idPoolToBeDeleted);
252                 if (LOG.isDebugEnabled()) {
253                     LOG.debug("Deleted id pool {}", poolName);
254                 }
255             }
256             deleteIdPoolRpcBuilder = RpcResultBuilder.success();
257         }
258         catch (Exception ex) {
259             LOG.error("Delete id in pool {} failed due to {}", poolName, ex);
260             deleteIdPoolRpcBuilder = RpcResultBuilder.failed();
261             deleteIdPoolRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
262         }
263         return Futures.immediateFuture(deleteIdPoolRpcBuilder.build());
264     }
265
266     @Override
267     public Future<RpcResult<Void>> releaseId(ReleaseIdInput input) {
268         String poolName = input.getPoolName();
269         String idKey = input.getIdKey();
270         RpcResultBuilder<Void> releaseIdRpcBuilder;
271         try {
272             releaseIdFromLocalPool(poolName, IdUtils.getLocalPoolName(poolName), idKey);
273             releaseIdRpcBuilder = RpcResultBuilder.success();
274         } catch (Exception ex) {
275             LOG.error("Release id {} from pool {} failed due to {}", idKey, poolName, ex);
276             releaseIdRpcBuilder = RpcResultBuilder.failed();
277             releaseIdRpcBuilder.withError(ErrorType.APPLICATION, ex.getMessage());
278         }
279         return Futures.immediateFuture(releaseIdRpcBuilder.build());
280     }
281
282     private List<Long> allocateIdFromLocalPool(String parentPoolName, String localPoolName, String idKey, long size) {
283         if (LOG.isDebugEnabled()) {
284             LOG.debug("Allocating id from local pool {}. Parent pool {}. Idkey {}", localPoolName, parentPoolName, idKey);
285         }
286         long newIdValue = -1;
287         List<Long> newIdValuesList = new ArrayList<>();
288         localPoolName = localPoolName.intern();
289         InstanceIdentifier<IdPool> parentIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(parentPoolName);
290         InstanceIdentifier<IdEntries> existingId = IdUtils.getIdEntry(parentIdPoolInstanceIdentifier, idKey);
291         Optional<IdEntries> existingIdEntry = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, existingId);
292         if (existingIdEntry.isPresent()) {
293             newIdValuesList = existingIdEntry.get().getIdValue();
294             if (LOG.isDebugEnabled()) {
295                 LOG.debug("Existing ids {} for the key {} ", newIdValuesList, idKey);
296             }
297             return newIdValuesList;
298         }
299         IdLocalPool localIdPool = localPool.get(parentPoolName);
300         if (localIdPool == null) {
301             IdUtils.lockPool(lockManager, parentPoolName);
302             try {
303                 WriteTransaction tx = broker.newWriteOnlyTransaction();
304                 IdPool parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
305                 localIdPool = createLocalPool(tx, localPoolName, parentIdPool); // Return localIdPool.....
306                 submitTransaction(tx);
307             } finally {
308                 IdUtils.unlockPool(lockManager, parentPoolName);
309             }
310         }
311         if (LOG.isDebugEnabled()) {
312             LOG.debug("Got pool {}", localIdPool);
313         }
314         if (size == 1) {
315             newIdValue = getIdFromLocalPoolCache(localIdPool, parentPoolName);
316             newIdValuesList.add(newIdValue);
317         } else {
318             IdPool parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
319             long totalAvailableIdCount = localIdPool.getAvailableIds().getAvailableIdCount() + localIdPool.getReleasedIds().getAvailableIdCount();
320             AvailableIdsHolderBuilder availableParentIds = IdUtils.getAvailableIdsHolderBuilder(parentIdPool);
321             ReleasedIdsHolderBuilder releasedParentIds = IdUtils.getReleaseIdsHolderBuilder(parentIdPool);
322             totalAvailableIdCount = totalAvailableIdCount + releasedParentIds.getAvailableIdCount()
323                     + IdUtils.getAvailableIdsCount(availableParentIds);
324             if (totalAvailableIdCount > size) {
325                 while (size > 0) {
326                     try {
327                         newIdValue = getIdFromLocalPoolCache(localIdPool, parentPoolName);
328                     } catch (RuntimeException e) {
329                         if (LOG.isDebugEnabled()) {
330                             LOG.debug("Releasing IDs to pool {}", localPoolName);
331                         }
332                         //Releasing the IDs added in newIdValuesList since a null list would be returned now, as the
333                         //requested size of list IDs exceeds the number of available IDs.
334                         updateDelayedEntriesInLocalCache(newIdValuesList, parentPoolName, localIdPool);
335                     }
336                     newIdValuesList.add(newIdValue);
337                     size--;
338                 }
339             } else {
340                 return null;
341             }
342         }
343         if (LOG.isDebugEnabled()) {
344             LOG.debug("The newIdValues {} for the idKey {}", newIdValuesList, idKey);
345         }
346         UpdateIdEntryJob job = new UpdateIdEntryJob(parentPoolName, localPoolName, idKey, newIdValuesList, broker);
347         DataStoreJobCoordinator.getInstance().enqueueJob(parentPoolName, job, IdUtils.RETRY_COUNT);
348         return newIdValuesList;
349     }
350
351     private Long getIdFromLocalPoolCache(IdLocalPool localIdPool, String parentPoolName) {
352         while (true) {
353             IdHolder releasedIds = localIdPool.getReleasedIds();
354             Optional<Long> releasedId = releasedIds.allocateId();
355             if (releasedId.isPresent()) {
356                 IdHolderSyncJob poolSyncJob = new IdHolderSyncJob(localIdPool.getPoolName(), releasedIds, broker);
357                 DataStoreJobCoordinator.getInstance().enqueueJob(localIdPool.getPoolName(), poolSyncJob, IdUtils.RETRY_COUNT);
358                 return releasedId.get();
359             }
360             IdHolder availableIds = localIdPool.getAvailableIds();
361             if (availableIds != null) {
362                 Optional<Long> availableId = availableIds.allocateId();
363                 if (availableId.isPresent()) {
364                     IdHolderSyncJob poolSyncJob = new IdHolderSyncJob(localIdPool.getPoolName(), availableIds, broker);
365                     DataStoreJobCoordinator.getInstance().enqueueJob(localIdPool.getPoolName(), poolSyncJob, IdUtils.RETRY_COUNT);
366                     return availableId.get();
367                 }
368             }
369             long idCount = getIdBlockFromParentPool(parentPoolName, localIdPool);
370             if (idCount <= 0) {
371                 if (LOG.isDebugEnabled()) {
372                     LOG.debug("Unable to allocate Id block from global pool");
373                 }
374                 throw new RuntimeException(String.format("Ids exhausted for pool : %s", parentPoolName));
375             }
376         }
377     }
378
379     /**
380      * Changes made to availableIds and releasedIds will not be persisted to the datastore
381      * @param parentPoolName
382      * @param availableIdsBuilder
383      * @param releasedIdsBuilder
384      * @return
385      */
386     private long getIdBlockFromParentPool(String parentPoolName, IdLocalPool localIdPool) {
387         if (LOG.isDebugEnabled()) {
388             LOG.debug("Allocating block of id from parent pool {}", parentPoolName);
389         }
390         InstanceIdentifier<IdPool> idPoolInstanceIdentifier = IdUtils.getIdPoolInstance(parentPoolName);
391         parentPoolName = parentPoolName.intern();
392         IdUtils.lockPool(lockManager, parentPoolName);
393         try {
394             WriteTransaction tx = broker.newWriteOnlyTransaction();
395             IdPool parentIdPool = getIdPool(idPoolInstanceIdentifier);
396             long idCount = allocateIdBlockFromParentPool(localIdPool, parentIdPool, tx);
397             submitTransaction(tx);
398             return idCount;
399         }
400         finally {
401             IdUtils.unlockPool(lockManager, parentPoolName);
402         }
403     }
404
405     private long allocateIdBlockFromParentPool(IdLocalPool localPoolCache, IdPool parentIdPool, WriteTransaction tx) {
406         long idCount = -1;
407         ReleasedIdsHolderBuilder releasedIdsBuilderParent = IdUtils.getReleaseIdsHolderBuilder(parentIdPool);
408         while (true) {
409             idCount = allocateIdBlockFromReleasedIdsHolder(localPoolCache, releasedIdsBuilderParent, parentIdPool, tx);
410             if (idCount > 0) {
411                 return idCount;
412             }
413             idCount = allocateIdBlockFromAvailableIdsHolder(localPoolCache, parentIdPool, tx);
414             if (idCount > 0) {
415                 return idCount;
416             }
417             idCount = getIdsFromOtherChildPools(releasedIdsBuilderParent, parentIdPool);
418             if (idCount <= 0) {
419                 if (LOG.isDebugEnabled()) {
420                     LOG.debug("Unable to allocate Id block from global pool");
421                 }
422                 throw new RuntimeException(String.format("Ids exhausted for pool : %s", parentIdPool.getPoolName()));
423             }
424         }
425     }
426
427     private long getIdsFromOtherChildPools(ReleasedIdsHolderBuilder releasedIdsBuilderParent, IdPool parentIdPool) {
428         List<ChildPools> childPoolsList = parentIdPool.getChildPools();
429         // Sorting the child pools on last accessed time so that the pool that was not accessed for a long time comes first.
430         Collections.sort(childPoolsList, new Comparator<ChildPools>() {
431             @Override
432             public int compare(ChildPools childPool1, ChildPools childPool2) {
433                 return childPool1.getLastAccessTime().compareTo(childPool2.getLastAccessTime());
434             }
435         });
436         long currentTime = System.currentTimeMillis() / 1000;
437         for (ChildPools childPools : childPoolsList) {
438             if (childPools.getLastAccessTime() + DEFAULT_IDLE_TIME > currentTime) {
439                 break;
440             }
441             if (!childPools.getChildPoolName().equals(IdUtils.getLocalPoolName(parentIdPool.getPoolName()))) {
442                 InstanceIdentifier<IdPool> idPoolInstanceIdentifier = IdUtils.getIdPoolInstance(childPools.getChildPoolName());
443                 IdPool otherChildPool = getIdPool(idPoolInstanceIdentifier);
444                 ReleasedIdsHolderBuilder releasedIds = IdUtils.getReleaseIdsHolderBuilder(otherChildPool);
445                 AvailableIdsHolderBuilder availableIds = IdUtils.getAvailableIdsHolderBuilder(otherChildPool);
446                 long totalAvailableIdCount = releasedIds.getDelayedIdEntries().size() + IdUtils.getAvailableIdsCount(availableIds);
447                 List<DelayedIdEntries> delayedIdEntriesChild = releasedIds.getDelayedIdEntries();
448                 List<DelayedIdEntries> delayedIdEntriesParent = releasedIdsBuilderParent.getDelayedIdEntries();
449                 if (delayedIdEntriesParent == null) {
450                     delayedIdEntriesParent = new LinkedList<>();
451                 }
452                 delayedIdEntriesParent.addAll(delayedIdEntriesChild);
453                 delayedIdEntriesChild.removeAll(delayedIdEntriesChild);
454                 while (IdUtils.isIdAvailable(availableIds)) {
455                     long cursor = availableIds.getCursor() + 1;
456                     delayedIdEntriesParent.add(IdUtils.createDelayedIdEntry(cursor, currentTime));
457                     availableIds.setCursor(cursor);
458                 }
459                 long count = releasedIdsBuilderParent.getAvailableIdCount() + totalAvailableIdCount;
460                 releasedIdsBuilderParent.setDelayedIdEntries(delayedIdEntriesParent).setAvailableIdCount(count);
461                 MDSALUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier,
462                         new IdPoolBuilder().setKey(new IdPoolKey(otherChildPool.getPoolName())).setAvailableIdsHolder(availableIds.build()).setReleasedIdsHolder(releasedIds.build()).build());
463                 return totalAvailableIdCount;
464             }
465         }
466         return 0;
467     }
468
469     private long allocateIdBlockFromReleasedIdsHolder(IdLocalPool localIdPool, ReleasedIdsHolderBuilder releasedIdsBuilderParent, IdPool parentIdPool, WriteTransaction tx) {
470         if (releasedIdsBuilderParent.getAvailableIdCount() == 0) {
471             if (LOG.isDebugEnabled()) {
472                 LOG.debug("Ids unavailable in releasedIds of parent pool {}", parentIdPool);
473             }
474             return 0;
475         }
476         List<DelayedIdEntries> delayedIdEntriesParent = releasedIdsBuilderParent.getDelayedIdEntries();
477         int idCount = Math.min(delayedIdEntriesParent.size(), parentIdPool.getBlockSize());
478         List<DelayedIdEntries> idEntriesToBeRemoved = delayedIdEntriesParent.subList(0, idCount);
479         ReleasedIdHolder releasedIds = (ReleasedIdHolder) localIdPool.getReleasedIds();
480         List<DelayedIdEntry> delayedIdEntriesLocalCache = releasedIds.getDelayedEntries();
481         delayedIdEntriesLocalCache = idEntriesToBeRemoved
482                 .parallelStream()
483                 .map(delayedIdEntry -> new DelayedIdEntry(delayedIdEntry
484                         .getId(), delayedIdEntry.getReadyTimeSec()))
485                 .sorted(new Comparator<DelayedIdEntry>() {
486                     @Override
487                     public int compare(DelayedIdEntry idEntry1,
488                             DelayedIdEntry idEntry2) {
489                         return Long.compare(idEntry1.getReadyTimeSec(),
490                                 idEntry2.getReadyTimeSec());
491                     }
492                 }).collect(Collectors.toList());
493         releasedIds.setDelayedEntries(delayedIdEntriesLocalCache);
494         releasedIds.setAvailableIdCount(releasedIds.getAvailableIdCount() + idCount);
495         localIdPool.setReleasedIds(releasedIds);
496         delayedIdEntriesParent.removeAll(idEntriesToBeRemoved);
497         releasedIdsBuilderParent.setDelayedIdEntries(delayedIdEntriesParent);
498         InstanceIdentifier<ReleasedIdsHolder> releasedIdsHolderInstanceIdentifier = InstanceIdentifier
499                 .builder(IdPools.class).child(IdPool.class,
500                         new IdPoolKey(parentIdPool.getPoolName())).child(ReleasedIdsHolder.class).build();
501         releasedIdsBuilderParent.setAvailableIdCount(releasedIdsBuilderParent.getAvailableIdCount() - idCount);
502         if (LOG.isDebugEnabled()) {
503             LOG.debug("Allocated {} ids from releasedIds of parent pool {}", idCount, parentIdPool);
504         }
505         tx.merge(LogicalDatastoreType.CONFIGURATION, releasedIdsHolderInstanceIdentifier, releasedIdsBuilderParent.build(), true);
506         return idCount;
507     }
508
509     private long allocateIdBlockFromAvailableIdsHolder(IdLocalPool localIdPool, IdPool parentIdPool, WriteTransaction tx) {
510         long idCount = 0;
511         AvailableIdsHolderBuilder availableIdsBuilderParent = IdUtils.getAvailableIdsHolderBuilder(parentIdPool);
512         long end = availableIdsBuilderParent.getEnd();
513         long cur = availableIdsBuilderParent.getCursor();
514         if (!IdUtils.isIdAvailable(availableIdsBuilderParent)) {
515             if (LOG.isDebugEnabled()) {
516                 LOG.debug("Ids exhausted in parent pool {}", parentIdPool);
517             }
518             return idCount;
519         }
520         // Update availableIdsHolder of Local Pool
521         idCount = Math.min(end - cur, parentIdPool.getBlockSize());
522         AvailableIdHolder availableIds = new AvailableIdHolder(cur + 1, cur + idCount);
523         localIdPool.setAvailableIds(availableIds);
524         // Update availableIdsHolder of Global Pool
525         InstanceIdentifier<AvailableIdsHolder> availableIdsHolderInstanceIdentifier = InstanceIdentifier
526                 .builder(IdPools.class).child(IdPool.class,
527                         new IdPoolKey(parentIdPool.getPoolName())).child(AvailableIdsHolder.class).build();
528         availableIdsBuilderParent.setCursor(cur + idCount);
529         if (LOG.isDebugEnabled()) {
530             LOG.debug("Allocated {} ids from availableIds of global pool {}", idCount, parentIdPool);
531         }
532         tx.merge(LogicalDatastoreType.CONFIGURATION, availableIdsHolderInstanceIdentifier, availableIdsBuilderParent.build(), true);
533         return idCount;
534     }
535
536     private void releaseIdFromLocalPool(String parentPoolName, String localPoolName, String idKey) {
537         localPoolName = localPoolName.intern();
538         InstanceIdentifier<IdPool> parentIdPoolInstanceIdentifier = IdUtils.getIdPoolInstance(parentPoolName);
539         IdPool parentIdPool = getIdPool(parentIdPoolInstanceIdentifier);
540         List<IdEntries> idEntries = parentIdPool.getIdEntries();
541         List<IdEntries> newIdEntries = idEntries;
542         if (idEntries == null) {
543             throw new RuntimeException("Id Entries does not exist");
544         }
545         InstanceIdentifier<IdEntries> existingId = IdUtils.getIdEntry(parentIdPoolInstanceIdentifier, idKey);
546         Optional<IdEntries> existingIdEntryObject = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, existingId);
547         if (!existingIdEntryObject.isPresent()) {
548             throw new RuntimeException(String.format("Specified Id key %s does not exist in id pool %s", idKey, parentPoolName));
549         }
550         IdEntries existingIdEntry = existingIdEntryObject.get();
551         List<Long> idValuesList = existingIdEntry.getIdValue();
552         IdLocalPool localIdPoolCache = localPool.get(parentPoolName);
553         boolean isRemoved = newIdEntries.remove(existingIdEntry);
554         if (LOG.isDebugEnabled()) {
555             LOG.debug("The entry {} is removed {}", existingIdEntry, isRemoved);
556         }
557         updateDelayedEntriesInLocalCache(idValuesList, parentPoolName, localIdPoolCache);
558         IdHolderSyncJob poolSyncJob = new IdHolderSyncJob(localPoolName, localIdPoolCache.getReleasedIds(), broker);
559         DataStoreJobCoordinator.getInstance().enqueueJob(localPoolName, poolSyncJob, IdUtils.RETRY_COUNT);
560         scheduleCleanUpTask(localIdPoolCache, parentPoolName, parentIdPool.getBlockSize());
561         if (LOG.isDebugEnabled()) {
562             LOG.debug("Released id ({}, {}) from pool {}", idKey, idValuesList, localPoolName);
563         }
564         //Updating id entries in the parent pool. This will be used for restart scenario
565         UpdateIdEntryJob job = new UpdateIdEntryJob(parentPoolName, localPoolName, idKey, null, broker);
566         DataStoreJobCoordinator.getInstance().enqueueJob(parentPoolName, job, IdUtils.RETRY_COUNT);
567     }
568
569     private void scheduleCleanUpTask(final IdLocalPool localIdPoolCache,
570             final String parentPoolName, final int blockSize) {
571         TimerTask scheduledTask = new TimerTask() {
572             @Override
573             public void run() {
574                 CleanUpJob job = new CleanUpJob(localIdPoolCache, broker, parentPoolName, blockSize, lockManager);
575                 DataStoreJobCoordinator.getInstance().enqueueJob(localIdPoolCache.getPoolName(), job, IdUtils.RETRY_COUNT);
576             }
577         };
578         cleanJobTimer.schedule(scheduledTask, IdUtils.DEFAULT_DELAY_TIME * 1000);
579     }
580
581     private IdPool createGlobalPool(WriteTransaction tx, String poolName, long low, long high, long blockSize) {
582         IdPool idPool;
583         InstanceIdentifier<IdPool> idPoolInstanceIdentifier = IdUtils.getIdPoolInstance(poolName);
584         Optional<IdPool> existingIdPool = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier);
585         if (!existingIdPool.isPresent()) {
586             if (LOG.isDebugEnabled()) {
587                 LOG.debug("Creating new global pool {}", poolName);
588             }
589             idPool = IdUtils.createGlobalPool(poolName, low, high, blockSize);
590             tx.put(LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier, idPool, true);
591         }
592         else {
593             idPool = existingIdPool.get();
594             if (LOG.isDebugEnabled()) {
595                 LOG.debug("GlobalPool exists {}", idPool);
596             }
597         }
598         return idPool;
599     }
600
601     IdLocalPool createLocalPool(WriteTransaction tx, String localPoolName, IdPool idPool) {
602         localPoolName = localPoolName.intern();
603         IdLocalPool idLocalPool = new IdLocalPool(localPoolName);
604         allocateIdBlockFromParentPool(idLocalPool, idPool, tx);
605         String parentPool = idPool.getPoolName();
606         localPool.put(parentPool, idLocalPool);
607         LocalPoolCreateJob job = new LocalPoolCreateJob(idLocalPool, broker, idPool.getPoolName(), idPool.getBlockSize());
608         DataStoreJobCoordinator.getInstance().enqueueJob(localPoolName, job, IdUtils.RETRY_COUNT);
609         return idLocalPool;
610     }
611
612     private void deletePool(String poolName) {
613         LocalPoolDeleteJob job = new LocalPoolDeleteJob(poolName, broker);
614         DataStoreJobCoordinator.getInstance().enqueueJob(poolName, job, IdUtils.RETRY_COUNT);
615     }
616
617     private IdPool getIdPool(InstanceIdentifier<IdPool> idPoolInstanceIdentifier) {
618         Optional<IdPool> idPool = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, idPoolInstanceIdentifier);
619         if (!idPool.isPresent()) {
620             throw new NoSuchElementException(String.format("Specified pool %s does not exist" , idPool));
621         }
622         if (LOG.isDebugEnabled()) {
623             LOG.debug("GetIdPool : Read id pool {} ", idPool);
624         }
625         return idPool.get();
626     }
627
628     public void poolDeleted(String parentPoolName, String poolName) {
629         IdLocalPool idLocalPool = localPool.get(parentPoolName);
630         if (idLocalPool != null) {
631             if (idLocalPool.getPoolName().equals(poolName)) {
632                 localPool.remove(parentPoolName);
633             }
634         }
635     }
636
637     private void submitTransaction(WriteTransaction tx) {
638         CheckedFuture<Void, TransactionCommitFailedException> futures = tx.submit();
639         try {
640             futures.get();
641         } catch (InterruptedException | ExecutionException e) {
642             LOG.error("Error writing to datastore tx", tx);
643             throw new RuntimeException(e.getMessage());
644         }
645     }
646
647     private void updateDelayedEntriesInLocalCache(List<Long> idsList, String parentPoolName, IdLocalPool localPoolCache) {
648         for(long idValue : idsList) {
649             localPoolCache.getReleasedIds().addId(idValue);
650         }
651         localPool.put(parentPoolName, localPoolCache);
652     }
653 }