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