summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe Fox in the Shell <KellerFuchs@hashbang.sh>2017-04-15 04:04:40 +0000
committerMykola Bilokonsky <mbilokonsky@gmail.com>2017-04-15 06:04:40 +0200
commitca9c013cdf700617199e1c97ac8ec9cb08486da4 (patch)
treee60dd057d50808e00fe9cdd6930ec9d127f5f130
parent32f8e690dd9eaf6a23dec8de072209cfc7149107 (diff)
Better boost cache (#4)
* boost(): Avoid unnecessary projection

* Abstraction for the cache of boosted toots

* boosted: Use per-creation-time bucketing

This allows to efficiently remove toots older than 5 days from the set,
without having to entirely clear the set (and spike API use instead).

Closes #3

* index.js: Use ES2015 style for concision

* boosted: Avoid double-negation in methods

“(not) already boosted” is more primitive than “(not) not already boosted”.
-rw-r--r--index.js58
1 files changed, 39 insertions, 19 deletions
diff --git a/index.js b/index.js
index d655618..62f0ed9 100644
--- a/index.js
+++ b/index.js
@@ -50,36 +50,56 @@ var M = new mastodon({
   api_url: process.env.INSTANCE_HOST + '/api/v1'
 });
 
-var boosted = {};
+var boosted = (function() {
+  const bucketSpan = 3600; // 1 hour buckets => up to 121 buckets over 5 days
+  const buckets = new Map();
 
-function clearCache() {
-  boosted = {};
-}
+  function prune() {
+    // Bucket id for 5 days ago
+    const threshold = (Date.now() - 5 * 24 * 3600 * 1000) / bucketSpan;
+
+    for (var bucket of buckets.keys()) {
+      if (bucket < threshold) buckets.delete(bucket);
+    }
+  }
+
+  function bucket(row) {
+    return row.created_at.getTime() / bucketSpan;
+  }
+
+  function already(row) {
+    const b = bucket(row);
+    return buckets.has(b) && buckets.get(b).has(row_id);
+  }
+
+  function set(row) {
+    const b = bucket(row);
+    if (!buckets.has(b)) buckets.set(b, new Set());
+    buckets.get(b).add(row_id);
+  }
+
+  return { already, prune, set };
+})();
 
 function boost(rows) {
-  rows.map(function(row) {
-    return row.id;
-  })
-  .filter(function(id) {
-    return !boosted[id];
-  })
-  .forEach(function(id) {
-    M.post('/statuses/' + id + '/reblog', function(err, result) {
+  rows.filter(x => !boosted.already(x))
+  .forEach(function(row) {
+    M.post('/statuses/' + row.id + '/reblog', function(err, result) {
       if (err) {
         if (err.message === 'Validation failed: Reblog of status already exists') {
-          boosted[id] = true;
-          return console.log('Warning: tried to boost #' + id + ' but it had already been boosted by this account. Adding to cache.');
+          boosted.set(row);
+          return console.log('Warning: tried to boost #' + row.id + ' but it had already been boosted by this account. Adding to cache.');
         }
 
         return console.log(err.message);
       }
-      boosted[id] = true;
-      console.log('boosted status #' + id);
+      boosted.set(row);
+      console.log('boosted status #' + row.id);
     });
   })
 }
 
 cycle();
-// clear that 'cache' daily, 2 seconds before the hour (since cycle runs on the hour)
-setInterval(clearCache, (1000 * 60 * 60 * 24) - 2000); 
-setInterval(cycle, 1000 * 60 * 15);
\ No newline at end of file
+// Prune the set of boosted toots every hour
+setInterval(boosted.prune, 1000 * 3600);
+setInterval(cycle, 1000 * 60 * 15);