Experimental memcached protocol.

Only works with python 2 now
This commit is contained in:
Honza Kral
2013-08-11 02:17:20 +02:00
parent 366e15dee8
commit 56e314f5b0
4 changed files with 106 additions and 5 deletions
+1
View File
@@ -4,3 +4,4 @@ nose
coverage
mock
pyaml
pylibmc
+2 -1
View File
@@ -9,6 +9,7 @@ from elasticsearch.transport import Transport
from elasticsearch.connection_pool import ConnectionPool, ConnectionSelector, \
RoundRobinSelector
from elasticsearch.serializer import JSONSerializer
from elasticsearch.connection import Connection, RequestsHttpConnection
from elasticsearch.connection import Connection, RequestsHttpConnection, \
Urllib3HttpConnection, MemcachedConnection
from elasticsearch.exceptions import *
+65 -4
View File
@@ -45,11 +45,10 @@ class Connection(object):
def _pretty_json(data):
# pretty JSON in tracer curl logs
try:
data = json.dumps(json.loads(data), sort_keys=True, indent=2, separators=(',', ': '))
except ValueError:
return json.dumps(json.loads(data), sort_keys=True, indent=2, separators=(',', ': '))
except (ValueError, TypeError):
# non-json data or a bulk request
pass
return data
return repr(data)
logger.info(
'%s %s [status:%s request:%.3fs]', method, full_url,
@@ -149,3 +148,65 @@ class Urllib3HttpConnection(Connection):
raw_data, duration)
return response.status, raw_data
class MemcachedConnection(Connection):
transport_schema = 'memcached'
method_map = {
'PUT': 'set',
'POST': 'set',
'DELETE': 'delete',
'HEAD': 'get',
'GET': 'get',
}
def __init__(self, host='localhost', port=11211, **kwargs):
try:
import pylibmc
except ImportError:
raise ImproperlyConfigured("You need to install pylibmc to use the MemcachedConnection class.")
super(MemcachedConnection, self).__init__(host=host, port=port, **kwargs)
self.mc = pylibmc.Client(['%s:%s' % (host, port)],behaviors={"tcp_nodelay": True})
def perform_request(self, method, url, params=None, body=None, timeout=None):
url = self.url_prefix + url
if params:
url = '%s?%s' % (url, urlencode(params or {}))
full_url = self.host + url
mc_method = self.method_map.get(method, 'get')
start = time.time()
try:
status = 200
if mc_method == 'set':
# no response from set commands
response = ''
if not json.dumps(self.mc.set(url, body)):
status = 500
else:
response = self.mc.get(url)
duration = time.time() - start
if response:
response = response.decode('utf-8')
except Exception as e:
self.log_request_fail(method, full_url, time.time() - start, exception=e)
raise ConnectionError('N/A', str(e), e)
# try not to load the json every time
if response and response[0] == '{' and ('"status"' in response or '"error"' in response):
data = json.loads(response)
if 'status' in data:
status = data['status']
elif 'error' in data:
raise TransportError('N/A', data['error'])
if not (200 <= status < 300):
self.log_request_fail(method, url, duration, status)
self._raise_error(status, response)
self.log_request_success(method, full_url, url, body, status,
response, duration)
return status, response
@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
from elasticsearch import Elasticsearch, MemcachedConnection, NotFoundError
from elasticsearch.transport import ADDRESS_RE
from . import ElasticTestCase
from unittest import SkipTest
class TestMemcachedConnection(ElasticTestCase):
def setUp(self):
super(TestMemcachedConnection, self).setUp()
nodes = self.client.cluster.node_info()
for node_id, node_info in nodes["nodes"].items():
if 'memcached_address' in node_info:
connection_info = ADDRESS_RE.search(node_info['memcached_address']).groupdict()
self.mc_client = Elasticsearch(
[connection_info],
connection_class=MemcachedConnection
)
break
else:
raise SkipTest("No memcached plugin.")
def test_index(self):
self.mc_client.index("test_index", "test_type", {"answer": 42}, id=1)
self.assertTrue(self.client.exists("test_index", doc_type="test_type", id=1))
def test_get(self):
self.client.index("test_index", "test_type", {"answer": 42}, id=1)
self.assertEquals({"answer": 42}, self.mc_client.get("test_index", doc_type="test_type", id=1)["_source"])
def test_unicode(self):
self.mc_client.index("test_index", "test_type", {"answer": u"你好"}, id=u"你好")
self.assertEquals({"answer": u"你好"}, self.mc_client.get("test_index", doc_type="test_type", id=u"你好")["_source"])
def test_missing(self):
self.assertRaises(NotFoundError, self.mc_client.get, "test_index", doc_type="test_type", id=42)