Project

General

Profile

1
package eu.dnetlib.data.mdstore.modular.mongodb;
2

    
3
import java.util.ArrayList;
4
import java.util.Date;
5
import java.util.List;
6

    
7
import com.google.common.collect.Lists;
8
import com.mongodb.BasicDBList;
9
import com.mongodb.BasicDBObject;
10
import com.mongodb.DBObject;
11
import com.mongodb.WriteConcern;
12
import com.mongodb.client.FindIterable;
13
import com.mongodb.client.MongoCollection;
14
import com.mongodb.client.MongoDatabase;
15
import com.mongodb.client.MongoIterable;
16
import com.mongodb.client.model.Filters;
17
import com.mongodb.client.model.IndexOptions;
18
import com.mongodb.client.model.UpdateOptions;
19
import eu.dnetlib.data.mdstore.MDStoreServiceException;
20
import eu.dnetlib.data.mdstore.modular.connector.*;
21
import eu.dnetlib.data.mdstore.modular.mongodb.utils.MDStoreUtils;
22
import org.apache.commons.lang3.StringUtils;
23
import org.apache.commons.logging.Log;
24
import org.apache.commons.logging.LogFactory;
25
import org.bson.conversions.Bson;
26
import org.joda.time.DateTime;
27
import org.joda.time.Days;
28
import org.springframework.beans.factory.annotation.Required;
29

    
30
/**
31
 * The Class MDStoreTransactionManager.
32
 */
33
public class MDStoreTransactionManagerImpl implements MDStoreTransactionManager {
34

    
35
	/** The Constant log. */
36
	private static final Log log = LogFactory.getLog(MDStoreTransactionManagerImpl.class);
37

    
38
	/**
39
	 * The table name.
40
	 */
41
	private static String TABLE_NAME = "metadataManager";
42

    
43
	/** The max number of concurrent transactions per mdstore. */
44
	private int maxTransactions = 1;
45

    
46
	/**
47
	 * The db.
48
	 */
49
	private MongoDatabase db;
50

    
51
	/**
52
	 * The manager table.
53
	 */
54
	private MongoCollection<DBObject> managerTable;
55

    
56
	/** The expired days. */
57
	private int expiredDays;
58

    
59
	private final IndexOptions options = new IndexOptions().background(true);
60

    
61
	/**
62
	 * Bootstrap manager.
63
	 */
64
	private void bootstrapManager() {
65
		log.debug("Bootstrap Manager start");
66
		final MongoCollection<DBObject> metadataColl = db.getCollection("metadata", DBObject.class);
67
		final FindIterable<DBObject> values = metadataColl.find();
68
		this.setManagerTable(db.getCollection(TABLE_NAME, DBObject.class));
69
		for (DBObject object : values) {
70
			final String id = (String) object.get("mdId");
71
			String newId = id;
72
			if (id.contains("_")) {
73
				newId = StringUtils.substringBefore(id, "_");
74
			}
75
			final BasicDBObject input = new BasicDBObject();
76
			input.put("mdId", id);
77
			input.put("currentId", newId);
78
			input.put("expiring", new String[] {});
79
			input.put("transactions", new String[] {});
80
			getManagerTable().insertOne(input);
81
			log.debug(String.format("Added %s to Metadata Manager data structure", id));
82

    
83
		}
84
		final BasicDBObject ensureIndex = new BasicDBObject();
85
		ensureIndex.put("mdId", 1);
86
		log.debug("Create index in MetadaManager ");
87
		this.getManagerTable().createIndex(ensureIndex, options);
88
	}
89

    
90
	/**
91
	 * Gets the DBObject describing an mdstore. null if there is no mdstore with the given id.
92
	 *
93
	 * @param mdstoreID the mdStore identifier
94
	 * @return DBObject or null
95
	 */
96
	private DBObject getMDStoreAsDBObject(final String mdstoreID) throws MDStoreServiceException {
97
		final BasicDBObject query = new BasicDBObject();
98
		query.put("mdId", mdstoreID);
99
		final FindIterable<DBObject> it = this.getManagerTable().find(query);
100
		return it.first();
101
	}
102

    
103
	/**
104
	 * Verify consistency.
105
	 *
106
	 * @throws MDStoreServiceException
107
	 *             the MD store service exception
108
	 */
109
	@Override
110
	public void verifyConsistency() throws MDStoreServiceException {
111
		if (this.getManagerTable() == null) {
112
			if (!Lists.newArrayList(db.listCollectionNames()).contains(TABLE_NAME))
113
				bootstrapManager();
114
			else {
115
				this.setManagerTable(db.getCollection(TABLE_NAME, DBObject.class));
116
			}
117
		}
118
		if (this.getManagerTable() == null) throw new MDStoreServiceException("Something bad happen, unable to create managerTable");
119
	}
120

    
121
	/**
122
	 * {@inheritDoc}
123
	 *
124
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#createMDStore(java.lang.String)
125
	 */
126
	@Override
127
	public void createMDStore(final String mdId) throws MDStoreServiceException {
128
		log.debug("Creating new mdstore");
129
		verifyConsistency();
130
		String newId = mdId;
131
		if (mdId.contains("_")) {
132
			newId = StringUtils.substringBefore(mdId, "_");
133
		}
134
		final BasicDBObject instance = new BasicDBObject();
135
		instance.put("mdId", mdId);
136
		instance.put("currentId", newId);
137
		instance.put("expiring", new String[] {});
138
		getManagerTable().insertOne(instance);
139
	}
140

    
141
	/**
142
	 * {@inheritDoc}
143
	 *
144
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#dropMDStore(java.lang.String)
145
	 */
146
	@Override
147
	public void dropMDStore(final String mdId) throws MDStoreServiceException {
148
		verifyConsistency();
149
		log.debug("Droping MDStore: " + mdId);
150
		final BasicDBObject query = new BasicDBObject();
151
		query.put("mdId", mdId);
152
		final DBObject dropped = this.getManagerTable().findOneAndDelete(query);
153
		garbage();
154
		final String collectionName = (String) dropped.get("currentId");
155
		this.db.getCollection(collectionName).drop();
156
		this.db.getCollection("discarded-" + collectionName).drop();
157
	}
158

    
159
	/**
160
	 * {@inheritDoc}
161
	 *
162
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#getMDStoreCollection(java.lang.String)
163
	 */
164
	@Override
165
	public String getMDStoreCollection(final String mdId) throws MDStoreServiceException {
166
		verifyConsistency();
167
		DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
168
		if (mdstoreInfo != null)
169
			return (String) mdstoreInfo.get("currentId");
170
		else return null;
171
	}
172

    
173
	/**
174
	 * {@inheritDoc}
175
	 *
176
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#startTransaction(java.lang.String, boolean)
177
	 */
178
	@Override
179
	public String startTransaction(final String mdId, final boolean refresh) throws MDStoreServiceException {
180
		verifyConsistency();
181
		log.info("Start transaction for metadata store " + mdId);
182
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
183
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdId);
184
		String idCreation = StringUtils.substringBefore(mdId, "_");
185
		idCreation = idCreation + "::" + System.currentTimeMillis();
186

    
187
		BasicDBList values;
188
		if (mdstoreInfo.containsField("transactions")) {
189
			values = (BasicDBList) mdstoreInfo.get("transactions");
190
			if (values.size() > getMaxTransactions())
191
				throw new MDStoreServiceException("Cannot create more than " + getMaxTransactions() + " transactions, found: " + values.size() + ", mdId:"
192
						+ mdId);
193
		} else {
194
			values = new BasicDBList();
195
		}
196
		final BasicDBObject transactionMetadata = new BasicDBObject();
197
		transactionMetadata.put("id", idCreation);
198
		transactionMetadata.put("refresh", refresh);
199
		transactionMetadata.put("date", new Date());
200
		values.add(transactionMetadata);
201
		mdstoreInfo.put("transactions", values);
202
		this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
203
		return idCreation;
204
	}
205

    
206
	/**
207
	 * {@inheritDoc}
208
	 *
209
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#commit(java.lang.String, java.lang.String,
210
	 *      eu.dnetlib.data.mdstore.modular.connector.MDStore)
211
	 */
212
	@Override
213
	public boolean commit(final String transactionId, final String mdstoreId, final MDStore current) throws MDStoreServiceException {
214
		verifyConsistency();
215
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdstoreId);
216
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdstoreId);
217
		final BasicDBList transactions = (BasicDBList) mdstoreInfo.get("transactions");
218
		final DBObject transaction = findTransaction(transactions, transactionId);
219
		if (transaction == null) throw new MDStoreServiceException("Error, unable to find transaction with Id " + transactionId);
220
		final boolean refresh = (Boolean) transaction.get("refresh");
221
		transactions.remove(transaction);
222
		final String oldId = (String) mdstoreInfo.get("currentId");
223
		if (refresh) {
224
			mdstoreInfo.put("currentId", transactionId);
225
			final BasicDBList stillUsed = (BasicDBList) mdstoreInfo.get("expiring");
226
			if (stillUsed.size() == 0) {
227
				db.getCollection(oldId).drop();
228
				db.getCollection("discarded-" + oldId).drop();
229
			}
230
			log.debug("Replaced collection ");
231
		} else {
232
			log.debug("commit incremental ");
233
			updateIncremental(transactionId, oldId);
234
			db.getCollection(transactionId).drop();
235
			db.getCollection("discarded-" + transactionId).drop();
236
		}
237
		this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
238

    
239
		log.info("Committed transaction for metadata store " + mdstoreId);
240
		return true;
241
	}
242

    
243
	/**
244
	 * Find transaction.
245
	 *
246
	 * @param transactions
247
	 *            the transactions
248
	 * @param transactionId
249
	 *            the transaction id
250
	 * @return the DB object
251
	 */
252
	private DBObject findTransaction(final BasicDBList transactions, final String transactionId) {
253
		if (transactions.size() == 0) return null;
254
		for (Object tx : transactions) {
255
			final BasicDBObject transaction = (BasicDBObject) tx;
256
			final String id = (String) transaction.get("id");
257
			if (transactionId.equals(id)) return transaction;
258
		}
259
		return null;
260

    
261
	}
262

    
263
	/**
264
	 * Gets the db.
265
	 *
266
	 * @return the db
267
	 */
268
	public MongoDatabase getDb() {
269
		return db;
270
	}
271

    
272
	/**
273
	 * Sets the db.
274
	 *
275
	 * @param db
276
	 *            the db to set
277
	 */
278
	@Required
279
	public void setDb(final MongoDatabase db) {
280
		this.db = db;
281
	}
282

    
283
	/**
284
	 * {@inheritDoc}
285
	 *
286
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#readMdStore(java.lang.String)
287
	 */
288
	@Override
289
	public String readMdStore(final String mdStoreId) throws MDStoreServiceException {
290
		verifyConsistency();
291
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdStoreId);
292
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdStoreId);
293
		final String currentId = (String) mdstoreInfo.get("currentId");
294
		final BasicDBList values = (BasicDBList) mdstoreInfo.get("expiring");
295
		updateMdstoreUsed(values, currentId);
296
		this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
297
		return currentId;
298

    
299
	}
300

    
301

    
302
	/**
303
	 * Update mdstore used.
304
	 *
305
	 * @param values
306
	 *            the values
307
	 * @param mdId
308
	 *            the md id
309
	 */
310
	private void updateMdstoreUsed(final BasicDBList values, final String mdId) {
311
		if (values.size() > 0) {
312
			for (Object value : values) {
313
				final DBObject obj = (DBObject) value;
314
				final String id = (String) obj.get("id");
315
				if (mdId.equals(id)) {
316
					obj.put("lastRead", new Date());
317
					return;
318
				}
319
			}
320
		}
321
		final BasicDBObject readStore = new BasicDBObject();
322
		readStore.put("id", mdId);
323
		readStore.put("lastRead", new Date());
324
		values.add(readStore);
325
	}
326

    
327
	/**
328
	 * Gets the manager table.
329
	 *
330
	 * @return the managerTable
331
	 */
332
	public MongoCollection<DBObject> getManagerTable() {
333
		return managerTable;
334
	}
335

    
336
	/**
337
	 * Sets the manager table.
338
	 *
339
	 * @param managerTable
340
	 *            the managerTable to set
341
	 */
342
	public void setManagerTable(final MongoCollection<DBObject> managerTable) {
343
		this.managerTable = managerTable;
344
	}
345

    
346
	/*
347
	 * (non-Javadoc)
348
	 *
349
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#getInfoForCurrentMdStore(java.lang.String)
350
	 */
351
	@Override
352
	public MDStoreManagerInfo getInfoForCurrentMdStore(final String mdStoreId) throws MDStoreServiceException {
353
		verifyConsistency();
354
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdStoreId);
355
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdStoreId);
356
		final MDStoreManagerInfo result = new MDStoreManagerInfo();
357
		result.setCurrentId((String) mdstoreInfo.get("currentId"));
358
		result.setMdId((String) mdstoreInfo.get("mdId"));
359
		final BasicDBList values = (BasicDBList) mdstoreInfo.get("expiring");
360
		for (Object v : values) {
361
			final MDStoreExpiredInfo stillused = new MDStoreExpiredInfo();
362
			final DBObject value = (DBObject) v;
363
			stillused.setId((String) value.get("id"));
364
			stillused.setLastRead((Date) value.get("lastRead"));
365
			result.addExpiredItem(stillused);
366
		}
367
		final BasicDBList transactions = (BasicDBList) mdstoreInfo.get("transactions");
368
		if (transactions != null) {
369
			for (Object tx : transactions) {
370
				final MDStoreTransactionInfo transaction = new MDStoreTransactionInfo();
371
				final DBObject value = (DBObject) tx;
372
				final String transactionId = (String) value.get("id");
373
				transaction.setId(transactionId);
374
				transaction.setDate((Date) value.get("date"));
375
				transaction.setRefresh((Boolean) value.get("refresh"));
376
				transaction.setSize(db.getCollection(transactionId).count());
377
				result.addTransactionInfo(transaction);
378
			}
379
		}
380
		return result;
381
	}
382

    
383
	/*
384
	 * (non-Javadoc)
385
	 *
386
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#dropUsed(java.lang.String, java.lang.String)
387
	 */
388
	@Override
389
	public Boolean dropUsed(final String mdId, final String idToDrop) throws MDStoreServiceException {
390
		verifyConsistency();
391
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
392
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdId);
393
		return dropStore(mdstoreInfo, idToDrop, "expiring");
394
	}
395

    
396
	private boolean dropStore(DBObject mdstoreInfo, final String idToDrop, String transactionListName) throws MDStoreServiceException {
397
		final BasicDBList transactionList = (BasicDBList) mdstoreInfo.get(transactionListName);
398
		for (int i = 0; i < transactionList.size(); i++) {
399
			final DBObject value = (DBObject) transactionList.get(i);
400
			final String currentUsedId = (String) value.get("id");
401
			if (currentUsedId.equals(idToDrop)) {
402
				db.getCollection(idToDrop).drop();
403
				db.getCollection("discarded-" + idToDrop).drop();
404
				transactionList.remove(value);
405
				mdstoreInfo.put(transactionListName, transactionList);
406
				this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", mdstoreInfo.get("_id")), mdstoreInfo);
407
				return true;
408
			}
409
		}
410
		throw new MDStoreServiceException("Error, unable to drop collection " + idToDrop);
411
	}
412

    
413
	/*
414
	 * (non-Javadoc)
415
	 *
416
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#garbage()
417
	 */
418
	@Override
419
	public void garbage() throws MDStoreServiceException {
420
		verifyConsistency();
421
		log.info("Start garbage collection of MdStore");
422
		final FindIterable<DBObject> it = this.managerTable.find();
423
		int totalDeleted = 0;
424
		for (DBObject currentObject :  it){
425
			if (log.isDebugEnabled()) {
426
				log.debug("start to check id: " + currentObject.get("currentId"));
427
			}
428
			garbageExpiring(currentObject, (String) currentObject.get("currentId"));
429
			garbageTransactions(currentObject, (String) currentObject.get("currentId"));
430
			this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", currentObject.get("_id")), currentObject);
431
		}
432

    
433
		// DELETING Collection that are not in the metadataManager table
434
		MongoIterable<String> collections = this.db.listCollectionNames();
435
		for (String collection : collections) {
436
			if ((collection.length() > 30) && (!collection.contains("discarded-"))) {
437
				final DBObject item = getMetadataObjectForCollections(collection);
438

    
439
				if (shouldDelete(collection, item)) {
440
					if (log.isDebugEnabled()) {
441
						log.debug("delete collection: " + collection + " from mongo");
442
					}
443
					db.getCollection(collection).drop();
444
					db.getCollection("discarded-" + collection).drop();
445
					if (log.isDebugEnabled()) {
446
						log.debug("delete collection: discarded-" + collection + " from mongo");
447
					}
448
				}
449
			}
450
		}
451

    
452
		log.info("Complete garbage collection of MdStore, total store deleted: " + totalDeleted);
453
	}
454

    
455
	private DBObject getMetadataObjectForCollections(final String collectionName) {
456
		if (collectionName == null) return null;
457
		final String postfix = "_TURTdG9yZURTUmVzb3VyY2VzL01EU3RvcmVEU1Jlc291cmNlVHlwZQ==";
458
		final String tmp = collectionName.contains("discarded-") ? StringUtils.substringAfter(collectionName, "discarded-") : collectionName;
459
		final String collectionNameCleaned = StringUtils.substringBefore(tmp, "::") + postfix;
460

    
461
		//DBObject query = QueryBuilder.start("mdId").is(collectionNameCleaned).get();
462
		Bson query = Filters.eq("mdId", collectionNameCleaned);
463
		return this.managerTable.find(query).first();
464

    
465
	}
466

    
467
	private boolean shouldDelete(final String collectionName, final DBObject metadataManagerInstance) {
468
		log.debug("should delete instance "+metadataManagerInstance);
469
		if((metadataManagerInstance== null) || (metadataManagerInstance.get("currentId")== null)){
470
			log.debug("the instance has not currentID");
471
			return true;
472
		}
473
		String currentId = (String) metadataManagerInstance.get("currentId");
474
		if (collectionName.equals(currentId)) return false;
475
		BasicDBList expiringList = (BasicDBList) metadataManagerInstance.get("expiring");
476
		if (findInList(expiringList, collectionName, "id")) return false;
477
		BasicDBList transactionsList = (BasicDBList) metadataManagerInstance.get("transactions");
478
		return !findInList(transactionsList, collectionName, "id");
479
	}
480

    
481
	private boolean findInList(final BasicDBList list, final String object, final String tagname) {
482
		if (list == null) return false;
483
		for (Object o : list) {
484
			DBObject currentObject = (DBObject) o;
485
			final String id = (String) currentObject.get(tagname);
486
			if (id.equals(object)) return true;
487
		}
488
		return false;
489
	}
490

    
491
	/**
492
	 * Delete.
493
	 *
494
	 * @param list
495
	 *            the list
496
	 * @param toRemove
497
	 *            the to remove
498
	 */
499
	private void delete(final BasicDBList list, final List<DBObject> toRemove) {
500

    
501
		for (final DBObject obj : toRemove) {
502
			if (log.isDebugEnabled()) {
503
				log.debug("deleting " + obj);
504
			}
505
			list.remove(obj);
506
		}
507
	}
508

    
509
	/**
510
	 * Garbage transactions.
511
	 *
512
	 * @param currentObject
513
	 *            the current object
514
	 * @param currentId
515
	 *            the current id
516
	 */
517
	private void garbageTransactions(final DBObject currentObject, final String currentId) {
518
		if (log.isDebugEnabled()) {
519
			log.debug("Start garbage transactions ");
520
		}
521

    
522
		final BasicDBList expiring = (BasicDBList) currentObject.get("transactions");
523
		if ((expiring == null) || (expiring.size() <= getMaxTransactions())) return;
524

    
525
		List<DBObject> expiringList = Lists.newArrayList();
526

    
527
		for (Object o : expiring) {
528
			final DBObject cobj = (DBObject) o;
529
			if (cobj != null) {
530
				expiringList.add((DBObject) o);
531
			}
532

    
533
		}
534

    
535
		expiringList.sort(MDStoreUtils.getComparatorOnDate());
536

    
537
		List<DBObject> toRemove = Lists.newArrayList();
538
		int i = 0;
539

    
540
		// We should remove the k item less recent
541
		// where k = numberOftotalTransaction - maxNumberOfTransaction
542
		// k = numberOfItemToRemove
543

    
544
		while (((expiringList.size() - toRemove.size()) > getMaxTransactions()) || (i < expiringList.size())) {
545
			DBObject currentObj = expiringList.get(i++);
546
			String objectId = (String) currentObj.get("id");
547
			if (!objectId.equals(currentId)) {
548
				if (log.isDebugEnabled()) {
549
					log.debug("delete collection: " + objectId + " from mongo");
550
				}
551
				db.getCollection(objectId).drop();
552
				db.getCollection("discarded-" + objectId).drop();
553
				if (log.isDebugEnabled()) {
554
					log.debug("delete collection: discarded-" + objectId + " from mongo");
555
				}
556
				toRemove.add(currentObj);
557
			} else {
558
				if (log.isDebugEnabled()) {
559
					log.debug("Cannot remove transaction " + objectId + " because is the currentId: " + currentId);
560
				}
561
			}
562
		}
563

    
564
		delete(expiring, toRemove);
565
		log.info("Deleted " + toRemove.size() + " transactions, mdStore Id:" + currentObject.get("mdId"));
566
	}
567

    
568
	/**
569
	 * Garbage expiring.
570
	 *
571
	 * @param currentObject
572
	 *            the current object
573
	 * @param currentId
574
	 *            the current id
575
	 */
576
	private void garbageExpiring(final DBObject currentObject, final String currentId) {
577
		if (log.isDebugEnabled()) {
578
			log.debug("Start to search expiring mdstores for id: " + currentObject.get("mdId"));
579
		}
580
		final BasicDBList expiring = (BasicDBList) currentObject.get("expiring");
581
		final List<DBObject> toRemove = Lists.newArrayList();
582
		if (log.isDebugEnabled()) {
583
			if (expiring == null) {
584
				log.debug("expiring list is null");
585
			} else {
586
				log.debug("expiring list size is :" + expiring.size());
587
			}
588
		}
589
		if ((expiring == null) || (expiring.size() == 0)) {
590
			log.debug("Deleted  0  expired  collections, mdStore Id:" + currentObject.get("mdId"));
591
			return;
592
		}
593
		for (Object anExpiring : expiring) {
594
			final DBObject currentExpiredStore = (DBObject) anExpiring;
595
			final String currentUsedId = (String) currentExpiredStore.get("id");
596
			final Days d = getExpiringDays(currentExpiredStore, "lastRead");
597
			if (log.isDebugEnabled()) {
598
				log.debug("the store :" + currentId + " expired since " + d.getDays() + "days ");
599
			}
600
			// DELETE the collection where the last time they was read
601
			// is more than 3 days ago
602
			if (d.getDays() > getExpiredDays()) {
603
				if (!currentUsedId.equals(currentId)) {
604
					db.getCollection(currentUsedId).drop();
605
					db.getCollection("discarded-" + currentUsedId).drop();
606
					log.debug("deleted collection " + currentUsedId);
607
				}
608
				toRemove.add(currentExpiredStore);
609
			}
610
		}
611
		delete(expiring, toRemove);
612
		log.debug("Deleted expired " + toRemove.size() + "collections, mdStore Id:" + currentObject.get("mdId"));
613
	}
614

    
615
	/**
616
	 * Gets the expiring days.
617
	 *
618
	 * @param value
619
	 *            the value
620
	 * @param paramName
621
	 *            the param name
622
	 * @return the expiring days
623
	 */
624
	private Days getExpiringDays(final DBObject value, final String paramName) {
625
		final Date lastRead = (Date) value.get(paramName);
626
		final DateTime last = new DateTime(lastRead);
627
		final DateTime today = new DateTime();
628
		final Days d = Days.daysBetween(last, today);
629
		return d;
630
	}
631

    
632
	/**
633
	 * Gets the expired days.
634
	 *
635
	 * @return the expiredDays
636
	 */
637
	public int getExpiredDays() {
638
		if (this.expiredDays == 0) return 3;
639
		return expiredDays;
640
	}
641

    
642
	/**
643
	 * Sets the expired days.
644
	 *
645
	 * @param expiredDays
646
	 *            the expiredDays to set
647
	 */
648
	public void setExpiredDays(final int expiredDays) {
649
		this.expiredDays = expiredDays;
650
	}
651

    
652
	/**
653
	 * Update incremental.
654
	 *
655
	 * @param transactionId
656
	 *            the transaction id
657
	 * @param currentId
658
	 *            the current id
659
	 */
660
	private void updateIncremental(final String transactionId, final String currentId) {
661
		final MongoCollection<DBObject> transaction = db.getCollection(transactionId, DBObject.class);
662
		final MongoCollection<DBObject> mdstore = db.getCollection(currentId, DBObject.class);
663
		final FindIterable<DBObject> it = transaction.find().noCursorTimeout(true);
664
		for (DBObject currentObj : it) {
665
			final String id = (String) currentObj.get("id");
666
			BasicDBObject newObj = (BasicDBObject)((BasicDBObject) currentObj).copy();
667
			//Must not have _id in the new object
668
			newObj.remove("_id");
669
			//setting to journaled write concern to be sure that when the write returns everything has been flushed to disk (https://docs.mongodb.org/manual/faq/developers/#when-does-mongodb-write-updates-to-disk)
670
			//the explicit fsync command can't be run anymore: 'Command failed with error 13: 'fsync may only be run against the admin database.'
671
			final MongoCollection<DBObject> mdstoreWrite = mdstore.withWriteConcern(WriteConcern.JOURNALED);
672
			mdstoreWrite.replaceOne(new BasicDBObject("id", id), newObj, new UpdateOptions().upsert(true));
673
		}
674
	}
675

    
676
	/*
677
	 * (non-Javadoc)
678
	 *
679
	 * @see eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager#dropTransaction(java.lang.String, java.lang.String)
680
	 */
681
	@Override
682
	public Boolean dropTransaction(final String mdId, final String idToDrop) throws MDStoreServiceException {
683
		verifyConsistency();
684
		final DBObject mdstoreInfo = getMDStoreAsDBObject(mdId);
685
		if (mdstoreInfo == null) throw new MDStoreServiceException("Error, unable to find Mdstore with Id " + mdId);
686
		return dropStore(mdstoreInfo, idToDrop, "transactions");
687
	}
688

    
689
	public void garbageTransactionsOnStart() throws MDStoreServiceException {
690
		verifyConsistency();
691
		FindIterable<DBObject> it = this.managerTable.find();
692

    
693
		final List<String> currentMdStoreId = Lists.newArrayList();
694
		for (DBObject currentObject : it){
695
			currentMdStoreId.add((String) currentObject.get("currentId"));
696
			final BasicDBList transactions = (BasicDBList) currentObject.get("transactions");
697
			if ((transactions != null) && (transactions.size() > 0)) {
698
				for (Object tx : transactions) {
699
					final DBObject currentTransactions = (DBObject) tx;
700
					final String id = (String) currentTransactions.get("id");
701
					db.getCollection(id).drop();
702
					db.getCollection("discarded-" + id).drop();
703
					log.debug("deleted collection " + id);
704
				}
705
				currentObject.put("transactions", new BasicDBList());
706
				this.getManagerTable().findOneAndReplace(new BasicDBObject("_id", currentObject.get("_id")), currentObject);
707
			}
708
		}
709

    
710
		//DELETING ALL THE DISCARDED COLLECTION THAT DISCARDED COLLECTION OF THE CURRENT MDSTORE
711
		final ArrayList<String> collectionsNames = Lists.newArrayList(db.listCollectionNames());
712

    
713
		for (String item : collectionsNames) {
714
			if (item.startsWith("discarded-")) {
715
				final String currentCollection = StringUtils.substringAfter(item, "discarded-");
716
				if (!currentMdStoreId.contains(currentCollection)) {
717
					log.info("Deleting discarded collection :" + item);
718
					this.db.getCollection(item).drop();
719
				}
720
			}
721
		}
722
	}
723

    
724
	/**
725
	 * Gets the max transactions.
726
	 *
727
	 * @return the maxTransactions
728
	 */
729
	public int getMaxTransactions() {
730
		return maxTransactions;
731
	}
732

    
733
	/**
734
	 * Sets the max transactions.
735
	 *
736
	 * @param maxTransactions
737
	 *            the maxTransactions to set
738
	 */
739
	public void setMaxTransactions(final int maxTransactions) {
740
		this.maxTransactions = maxTransactions;
741
	}
742
}
(2-2/5)