From a877fa0fd809929b891299a9296215224af27fe3 Mon Sep 17 00:00:00 2001
From: James Kafader <jkafader@archive.org>
Date: Tue, 26 Sep 2017 15:51:11 -0700
Subject: [PATCH] Adds cron garbage collector

---
 doublethink/services.py         |  5 +++++
 scripts/purge_stale_services.py | 35 +++++++++++++++++++++++++++++++++
 setup.py                        |  1 +
 3 files changed, 41 insertions(+)
 create mode 100644 scripts/purge_stale_services.py

diff --git a/doublethink/services.py b/doublethink/services.py
index 38a0f93..616b306 100644
--- a/doublethink/services.py
+++ b/doublethink/services.py
@@ -312,3 +312,8 @@ class ServiceRegistry(object):
     available_service = healthy_service
     available_services = healthy_services
 
+    def purge_stale_services(self, grace_period=0):
+        result = self.rr.table('services').filter(
+                lambda svc: r.now().sub(svc["last_heartbeat"]).gt(svc["ttl"] + grace_period)
+            ).delete().run()
+        return result
diff --git a/scripts/purge_stale_services.py b/scripts/purge_stale_services.py
new file mode 100644
index 0000000..31794c5
--- /dev/null
+++ b/scripts/purge_stale_services.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+import sys
+from optparse import OptionParser
+from doublethink import Rethinker
+from doublethink.services import ServiceRegistry
+
+usage = """usage: %prog [options] db
+where 'db' is the the name of a RethinkDB database that contains a "services" table.
+
+This script can be used to periodically purge stale entries from the "services" table.
+
+It is designed to be used in conjunction with cron.
+
+Example:
+%prog -s rethink-host0,rethink-host1,rethink-host2 doublethink_database"""
+parser = OptionParser(usage=usage)
+parser.add_option("-s", "--rethinkdb-servers",
+    metavar="SERVERS", dest="servers",
+    help="a comma-separated list of hostnames of rethinkdb servers. Required. [default: none]")
+parser.add_option("-g", "--grace-period",
+    metavar="SECONDS", dest="grace_period", type="int",
+    help="leave records that have been stale for up to SECONDS seconds. [default: 0]")
+(options, args) = parser.parse_args()
+
+if len(args) < 1:
+    sys.exit('"db" is a required argument and should be the name of a RethinkDB database that contains a "services" table. See "--help" for a list of options')
+
+if not options.servers:
+    sys.exit('--rethinkdb-servers (-s) is a required argument. It should be a comma-separated list of rethinkdb servers. See --help for more information')
+
+options.servers = [srv.strip() for srv in options.servers.split(",")]
+
+rethinker = Rethinker(servers=options.servers, db=args[0])
+registry = ServiceRegistry(rethinker)
+registry.purge_stale_services(grace_period=options.grace_period)
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 48d7ea7..e16e560 100644
--- a/setup.py
+++ b/setup.py
@@ -18,4 +18,5 @@ setuptools.setup(
     description='rethinkdb python library',
     long_description=codecs.open(
         'README.rst', mode='r', encoding='utf-8').read(),
+    scripts=glob.glob('scripts/*.py'),
 )