Skip to content

Commit 9271205

Browse files
author
hideki
committed
Fixed couchbase/couchbase-lite-java-core#1207
### TTL support Implemented methods for TTL features
1 parent b417041 commit 9271205

2 files changed

Lines changed: 44 additions & 39 deletions

File tree

src/main/java/com/couchbase/lite/Database.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@
6161
import java.util.Locale;
6262
import java.util.Map;
6363
import java.util.Set;
64+
import java.util.Timer;
65+
import java.util.TimerTask;
6466
import java.util.concurrent.CopyOnWriteArraySet;
6567
import java.util.concurrent.Future;
66-
import java.util.concurrent.TimeUnit;
6768
import java.util.concurrent.atomic.AtomicBoolean;
6869

6970
/**
@@ -126,6 +127,7 @@ public class Database implements StoreDelegate {
126127
private boolean postingChangeNotifications;
127128
private final Object lockPostingChangeNotifications = new Object();
128129
private final long startTime;
130+
private Timer purgeTimer;
129131

130132
/**
131133
* Each database can have an associated PersistentCookieStore,
@@ -1402,6 +1404,9 @@ public boolean close() {
14021404
// Clear all replicators:
14031405
allReplicators.clear();
14041406

1407+
// cancel purge timer
1408+
cancelPurgeTimer();
1409+
14051410
// Close Store:
14061411
if (store != null)
14071412
store.close();
@@ -2264,42 +2269,43 @@ private void installAttachment(AttachmentInternal attachment) throws CouchbaseLi
22642269
// #pragma mark - EXPIRATION:
22652270

22662271
/* package */void setExpirationDate(Date date, String docID) {
2267-
long timestamp = date != null ? date.getTime() : 0;
2268-
store.setExpirationOfDocument(timestamp, docID);
2272+
long unixTime = date != null ? date.getTime() / 1000 : 0;
2273+
store.setExpirationOfDocument(unixTime, docID);
22692274
scheduleDocumentExpiration(0);
22702275
}
22712276

22722277
private void scheduleDocumentExpiration(long minimumDelay) {
2278+
if (store == null) return;
2279+
22732280
long nextExpiration = store.nextDocumentExpiry();
22742281
if (nextExpiration > 0) {
2275-
long delay = Math.max(nextExpiration - System.currentTimeMillis() + 1, minimumDelay);
2276-
Log.v(TAG, "Scheduling next doc expiration in %d sec", delay / 1000);
2277-
manager.getWorkExecutor().schedule(new Runnable() {
2282+
long delay = Math.max((nextExpiration - System.currentTimeMillis()) / 1000 + 1, minimumDelay);
2283+
Log.v(TAG, "Scheduling next doc expiration in %d sec", delay);
2284+
cancelPurgeTimer();
2285+
purgeTimer = new Timer();
2286+
purgeTimer.schedule(new TimerTask() {
22782287
@Override
22792288
public void run() {
2280-
if(isOpen())
2289+
if (isOpen())
22812290
purgeExpiredDocuments();
22822291
}
2283-
}, delay, TimeUnit.MILLISECONDS);
2284-
} else {
2292+
}, delay * 1000);
2293+
} else
22852294
Log.v(TAG, "No pending doc expirations");
2286-
}
22872295
}
22882296

22892297
private void purgeExpiredDocuments() {
2290-
Log.v(TAG, "Purging expired documents...");
2298+
if (store == null) return;
22912299
int nPurged = store.purgeExpiredDocuments();
22922300
Log.v(TAG, "Purged %d expired documents", nPurged);
22932301
scheduleDocumentExpiration(1);
22942302
}
22952303

2296-
private void purgeExpiredDocumentsAsync() {
2297-
runAsync(new AsyncTask() {
2298-
@Override
2299-
public void run(Database database) {
2300-
purgeExpiredDocuments();
2301-
}
2302-
});
2304+
private void cancelPurgeTimer() {
2305+
if (purgeTimer != null) {
2306+
purgeTimer.cancel();
2307+
purgeTimer = null;
2308+
}
23032309
}
23042310

23052311
// #pragma mark - LOOKING UP ATTACHMENTS:

src/main/java/com/couchbase/lite/store/SQLiteStore.java

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,18 +1911,21 @@ private boolean purgeSequences(Set<Long> seqsToPurge) {
19111911
// EXPIRATION:
19121912
///////////////////////////////////////////////////////////////////////////
19131913

1914+
/**
1915+
* @return Java Time
1916+
*/
19141917
@Override
19151918
public long expirationOfDocument(String docID) {
19161919
return SQLiteUtils.longForQuery(storageEngine,
19171920
"SELECT expiry_timestamp FROM docs WHERE docid=?",
1918-
new String[]{docID});
1921+
new String[]{docID}) * 1000L;
19191922
}
19201923

19211924
@Override
1922-
public boolean setExpirationOfDocument(long timestamp, String docID) {
1925+
public boolean setExpirationOfDocument(long unixTime, String docID) {
19231926
try {
19241927
ContentValues values = new ContentValues();
1925-
values.put("expiry_timestamp", timestamp);
1928+
values.put("expiry_timestamp", unixTime);
19261929
String[] whereArgs = {docID};
19271930
int rowsUpdated = storageEngine.update("docs", values, "docid=?", whereArgs);
19281931
return rowsUpdated > 0 ? true : false;
@@ -1932,11 +1935,14 @@ public boolean setExpirationOfDocument(long timestamp, String docID) {
19321935
}
19331936
}
19341937

1938+
/**
1939+
* @return Java Time
1940+
*/
19351941
@Override
19361942
public long nextDocumentExpiry() {
19371943
return SQLiteUtils.longForQuery(storageEngine,
19381944
"SELECT MIN(expiry_timestamp) FROM docs WHERE expiry_timestamp not null and expiry_timestamp != 0",
1939-
null);
1945+
null) * 1000L;
19401946
}
19411947

19421948
@Override
@@ -1948,37 +1954,33 @@ public boolean run() {
19481954
if (storageEngine == null)
19491955
return false;
19501956

1951-
long now = System.currentTimeMillis();
1957+
long nowUnixTime = System.currentTimeMillis() / 1000L;
19521958
invalidateDocNumericIDs();
19531959

1954-
String[] args = {String.valueOf(now)};
1960+
String[] args = {String.valueOf(nowUnixTime)};
19551961

19561962
// First capture the docIDs to be purged, so we can notify about them:
19571963
List<String> purgedIDs = new ArrayList<String>();
1958-
String queryString = "SELECT docid FROM docs WHERE expiry_timestamp <= ?";
1964+
String queryString = "SELECT docid FROM docs WHERE expiry_timestamp <= ? and expiry_timestamp != 0";
19591965
Cursor cursor = storageEngine.rawQuery(queryString, args);
19601966
try {
1961-
if (cursor.moveToNext())
1967+
cursor.moveToNext();
1968+
while (!cursor.isAfterLast()) {
19621969
purgedIDs.add(cursor.getString(0));
1970+
cursor.moveToNext();
1971+
}
19631972
} finally {
19641973
cursor.close();
19651974
}
19661975

19671976
// Now delete the docs:
1968-
for (String docID : purgedIDs) {
1969-
String[] arg = {docID};
1970-
storageEngine.delete("docs", "docid = ?", arg);
1971-
}
1972-
/* NOTE: following codes are more efficient, but sometimes docIDs do not match with
1973-
previous query results.
19741977
try {
1975-
int count = storageEngine.delete("docs", "expiry_timestamp <= ?", args);
1976-
Log.e(TAG, "purged doc count: %d/%d", count, purgedIDs.size());
1978+
int count = storageEngine.delete("docs", "expiry_timestamp <= ? and expiry_timestamp != 0", args);
1979+
Log.v(TAG, "purged doc count: %d/%d", count, purgedIDs.size());
19771980
} catch (SQLException e) {
1978-
Log.w(TAG, "Failed to delete from docs expiry_timestamp <= %d", e, now);
1981+
Log.w(TAG, "Failed to delete from docs expiry_timestamp <= %d", e, nowUnixTime);
19791982
return false;
19801983
}
1981-
*/
19821984

19831985
// Finally notify:
19841986
for (String docID : purgedIDs)
@@ -1991,10 +1993,7 @@ public boolean run() {
19911993
return nPurged.get();
19921994
}
19931995

1994-
int i = 0;
1995-
19961996
private void notifyPurgedDocument(String docID) {
1997-
Log.e(TAG, "notifyPurgedDocument() [%d] docID=%s", i++, docID);
19981997
delegate.databaseStorageChanged(new DocumentChange(docID));
19991998
}
20001999

0 commit comments

Comments
 (0)