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