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