mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-27 03:00:12 -04:00
* Activate negative-result caching for HardCache chain
* begin migration to pylibmc:
* Add pylibmc to the list of required packages in preparation for the replacement of the memcached library
* Start using pylibmc for the rendercaches
* Tweak the computation of the normalized hot page to be a bit faster when the precomputer is available, by relying on the precomputer's internal permacached structure.
* Default to a SelfEmptyingCache for scripts run from `paster run'. Note that processes that run forever are still responsible for resetting their local-caches, but this can now be done with g.reset_caches()
* threaded messaging patch part 1: backend changes only. This will allow migrate.py to be run to move new onto inbox and will start tracking message trees for users.
* Specify some queries to run at most once per day
* Refactored safemarkdown() and added soup testing
Additions:
* Added _byID_rel()
* Made error messages more verbose for:
1. byID lookups of too-big thing_ids
2. memcache failures
3. Solr Nones
* Award._all_awards() now sorts by date
* Trophy.by_{account,award}() now cache properly
* new feedback page with helpful links
* Try to reduce the length of the query-queue by not adding known-long queries at all, rather than adding them and skipping them
* whitespace clean up
* simplify the 'why did my CC get denied' email checking.
* added missing translation strings and users now get PMs when they are added as translators
Bugfixes:
* Fix a bug in unsaving
* BeautifulSoup stopped hosting 3.0.7a, but 3.0.8 still uses the good parser
* Better search error handling
* Properly reset the cache-chains (incl. the hardcache; d'oh!) per-request
* Fix an attribute error on listings where some items have author_ids and some don't
* Bug when forcing recalculation of memoized functions
* the subreddit creation and edit form aren't dealing with errors properly
* buttons fix
164 lines
5.6 KiB
Python
164 lines
5.6 KiB
Python
# The contents of this file are subject to the Common Public Attribution
|
|
# License Version 1.0. (the "License"); you may not use this file except in
|
|
# compliance with the License. You may obtain a copy of the License at
|
|
# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
|
# License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
|
# software over a computer network and provide for limited attribution for the
|
|
# Original Developer. In addition, Exhibit A has been modified to be consistent
|
|
# with Exhibit B.
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
|
# the specific language governing rights and limitations under the License.
|
|
#
|
|
# The Original Code is Reddit.
|
|
#
|
|
# The Original Developer is the Initial Developer. The Initial Developer of the
|
|
# Original Code is CondeNet, Inc.
|
|
#
|
|
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
|
|
# CondeNet, Inc. All Rights Reserved.
|
|
################################################################################
|
|
import socket, cPickle as pickle
|
|
from threading import Thread
|
|
from SocketServer import DatagramRequestHandler, StreamRequestHandler, ThreadingMixIn, UDPServer, TCPServer
|
|
|
|
class CustomThreadingMixIn(ThreadingMixIn):
|
|
"""Mix-in class to handle each request in a new thread."""
|
|
|
|
def __init__(self, thread_class = Thread):
|
|
self.thread_class = thread_class
|
|
|
|
def process_request(self, request, client_address):
|
|
"""Start a new thread to process the request."""
|
|
t = self.thread_class(target = self.process_request_thread,
|
|
args = (request, client_address))
|
|
if self.daemon_threads:
|
|
t.setDaemon (1)
|
|
t.start()
|
|
|
|
|
|
class Responses:
|
|
OK, ERROR = range(2)
|
|
|
|
class SimpleHandler:
|
|
def handle(self):
|
|
try:
|
|
fn_name, a, kw = pickle.load(self.rfile)
|
|
fn = getattr(self.server.container, fn_name)
|
|
res = (Responses.OK, fn(*a, **kw))
|
|
except Exception, e:
|
|
res = (Responses.ERROR, e)
|
|
try:
|
|
self.wfile.write(pickle.dumps(res, -1))
|
|
except:
|
|
res = (Responses.ERROR, 'Error while pickling.' )
|
|
self.wfile.write(pickle.dumps(res, -1))
|
|
|
|
class SimpleUDPHandler(SimpleHandler, DatagramRequestHandler): pass
|
|
class SimpleTCPHandler(SimpleHandler, StreamRequestHandler): pass
|
|
|
|
class ThreadedUDPServer(CustomThreadingMixIn, UDPServer):
|
|
def __init__(self, server_address, RequestHandlerClass, container,
|
|
thread_class = Thread):
|
|
UDPServer.__init__(self, server_address, RequestHandlerClass)
|
|
CustomThreadingMixIn.__init__(self, thread_class)
|
|
self.container = container
|
|
self.daemon_threads = True
|
|
|
|
class ThreadedTCPServer(CustomThreadingMixIn, TCPServer):
|
|
def __init__(self, server_address, RequestHandlerClass, container,
|
|
thread_class = Thread):
|
|
self.allow_reuse_address = True
|
|
TCPServer.__init__(self, server_address, RequestHandlerClass)
|
|
CustomThreadingMixIn.__init__(self, thread_class)
|
|
self.container = container
|
|
self.daemon_threads = True
|
|
|
|
|
|
class Server:
|
|
def __init__(self, container, addr='', port=5000,
|
|
daemon=True, tcp=False, thread_class = Thread):
|
|
if tcp:
|
|
self.s = ThreadedTCPServer((addr, port), SimpleTCPHandler,
|
|
container, thread_class)
|
|
else:
|
|
self.s = ThreadedUDPServer((addr, port), SimpleUDPHandler,
|
|
container, thread_class)
|
|
|
|
self.handle_thread = thread_class(target = self.s.serve_forever)
|
|
self.handle_thread.setDaemon(daemon)
|
|
self.handle_thread.start()
|
|
|
|
|
|
class RemoteCall:
|
|
def __init__(self, client, response_required):
|
|
self.client = client
|
|
self.response_required = response_required
|
|
|
|
def __getattr__(self, attr):
|
|
def fn(*a, **kw):
|
|
return self.client.send(self.response_required, attr, a, kw)
|
|
return fn
|
|
|
|
class Client:
|
|
def __init__(self, host='localhost', port=5000, tcp=False):
|
|
self.conninfo = (host, port)
|
|
self.call = RemoteCall(self, True)
|
|
self.call_nr = RemoteCall(self, False)
|
|
self.tcp = tcp
|
|
|
|
def send(self, response_required, fn, a, kw):
|
|
msg = pickle.dumps((fn, a, kw), -1)
|
|
|
|
#get socket + send
|
|
if self.tcp:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
try:
|
|
s.connect(self.conninfo)
|
|
except:
|
|
return
|
|
s.send(msg)
|
|
else:
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.settimeout(10)
|
|
s.sendto(msg, self.conninfo)
|
|
|
|
if response_required:
|
|
infile = s.makefile('rb')
|
|
error_code, res = pickle.load(infile)
|
|
#close
|
|
try:
|
|
if self.tcp: s.close()
|
|
except: pass
|
|
#return
|
|
if error_code == Responses.OK:
|
|
return res
|
|
else:
|
|
raise Exception, res
|
|
elif self.tcp:
|
|
try:
|
|
s.close()
|
|
except: pass
|
|
|
|
class TH:
|
|
def add(self, x,y):
|
|
return x + y
|
|
|
|
def echo(self, str):
|
|
return str
|
|
|
|
#s = Server(TH)
|
|
#c = Client()
|
|
#print c.call.add(1,2)
|
|
|
|
def test_length(client):
|
|
x = 0
|
|
while True:
|
|
print len(client.call.echo(['x' for i in range(x)]))
|
|
x += 100
|
|
|
|
def perf_test(client):
|
|
for x in range(1000):
|
|
client.call.echo('test')
|