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