Browse Source

Add nodestore backend for Cassandra using casscache

Matt Robenolt 11 years ago
parent
commit
03310dee4b

+ 3 - 0
.travis.yml

@@ -5,6 +5,7 @@ services:
   - mysql
   - postgresql
   - redis-server
+  - cassandra
 python:
   - "2.6"
   - "2.7"
@@ -39,6 +40,8 @@ install:
 before_script:
   - mysql -e 'create database sentry;'
   - psql -c 'create database sentry;' -U postgres
+  - "echo \"create keyspace sentry with replication = {'class' : 'SimpleStrategy', 'replication_factor': 1};\" | cqlsh --cqlversion=3.0.3"
+  - echo 'create table nodestore (key text primary key, value blob, flags int);' | cqlsh -k sentry --cqlversion=3.0.3
 script:
   - make lint
   - make test-js

+ 2 - 0
setup.py

@@ -47,6 +47,8 @@ dev_requires = [
 ]
 
 tests_require = [
+    'casscache',
+    'cqlsh',
     'exam>=0.5.1',
     'eventlet',
     'httpretty',

+ 8 - 0
src/sentry/nodestore/cassandra/__init__.py

@@ -0,0 +1,8 @@
+"""
+sentry.nodestore.cassandra
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
+:license: BSD, see LICENSE for more details.
+"""
+from .backend import *  # NOQA

+ 46 - 0
src/sentry/nodestore/cassandra/backend.py

@@ -0,0 +1,46 @@
+"""
+sentry.nodestore.cassandra.backend
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:copyright: (c) 2010-2013 by the Sentry Team, see AUTHORS for more details.
+:license: BSD, see LICENSE for more details.
+"""
+
+from __future__ import absolute_import
+
+import casscache
+
+from sentry.nodestore.base import NodeStorage
+
+
+class CassandraNodeStorage(NodeStorage):
+    """
+    A Cassandra-based backend for storing node data.
+
+    >>> CassandraNodeStorage(
+    ...     servers=['127.0.0.1:9042'],
+    ...     keyspace='sentry',
+    ...     columnfamily='nodestore',
+    ... )
+    """
+    def __init__(self, servers, keyspace='sentry',
+                 columnfamily='nodestore', **kwargs):
+        self.conn = casscache.Client(
+            servers=servers,
+            keyspace=keyspace,
+            columnfamily=columnfamily,
+            **kwargs
+        )
+        super(CassandraNodeStorage, self).__init__(**kwargs)
+
+    def delete(self, id):
+        self.conn.delete(id)
+
+    def get(self, id):
+        return self.conn.get(id)
+
+    def get_multi(self, id_list):
+        return self.conn.get_multi(id_list)
+
+    def set(self, id, data):
+        self.conn.set(id, data)

+ 17 - 1
src/sentry/testutils.py

@@ -212,7 +212,8 @@ class BaseTestCase(Exam):
             secret = self.projectkey.secret_key
 
         message = self._makeMessage(data)
-        resp = self.client.post(reverse('sentry-api-store'), message,
+        resp = self.client.post(
+            reverse('sentry-api-store'), message,
             content_type='application/octet-stream',
             HTTP_X_SENTRY_AUTH=get_auth_header('_postWithHeader', key, secret),
         )
@@ -306,3 +307,18 @@ def riak_is_available():
 requires_riak = pytest.mark.skipif(
     lambda x: not riak_is_available(),
     reason="requires riak server running")
+
+
+def cassandra_is_available():
+    import socket
+    try:
+        socket.create_connection(('127.0.0.1', 9042), 1.0)
+    except socket.error:
+        return False
+    else:
+        return True
+
+
+requires_cassandra = pytest.mark.skipif(
+    lambda x: not cassandra_is_available(),
+    reason="requires cassandra server running")

+ 0 - 0
tests/sentry/nodestore/cassandra/__init__.py


+ 0 - 0
tests/sentry/nodestore/cassandra/backend/__init__.py


+ 41 - 0
tests/sentry/nodestore/cassandra/backend/tests.py

@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import absolute_import
+
+from sentry.nodestore.cassandra.backend import CassandraNodeStorage
+from sentry.testutils import TestCase, requires_cassandra
+
+
+@requires_cassandra
+class CassandraNodeStorageTest(TestCase):
+    def setUp(self):
+        self.ns = CassandraNodeStorage(servers=[
+            '127.0.0.1:9042',
+        ])
+
+    def test_integration(self):
+        node_id = self.ns.create({
+            'foo': 'bar',
+        })
+        assert node_id is not None
+
+        self.ns.set(node_id, {
+            'foo': 'baz',
+        })
+
+        result = self.ns.get(node_id)
+        assert result == {
+            'foo': 'baz',
+        }
+
+        node_id2 = self.ns.create({
+            'foo': 'bar',
+        })
+
+        result = self.ns.get_multi([node_id, node_id2])
+        assert result[node_id] == {
+            'foo': 'baz',
+        }
+        assert result[node_id2] == {
+            'foo': 'bar',
+        }