couchproxy 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +19 -0
- data/README +36 -0
- data/Rakefile +42 -0
- data/bin/couchproxy +88 -0
- data/conf/couchproxy.yml +21 -0
- data/lib/couchproxy/cluster.rb +43 -0
- data/lib/couchproxy/collator.rb +60 -0
- data/lib/couchproxy/deferrable_body.rb +15 -0
- data/lib/couchproxy/node.rb +25 -0
- data/lib/couchproxy/partition.rb +15 -0
- data/lib/couchproxy/rack/active_tasks.rb +9 -0
- data/lib/couchproxy/rack/all_databases.rb +23 -0
- data/lib/couchproxy/rack/all_docs.rb +9 -0
- data/lib/couchproxy/rack/base.rb +197 -0
- data/lib/couchproxy/rack/bulk_docs.rb +68 -0
- data/lib/couchproxy/rack/changes.rb +9 -0
- data/lib/couchproxy/rack/compact.rb +16 -0
- data/lib/couchproxy/rack/config.rb +16 -0
- data/lib/couchproxy/rack/database.rb +83 -0
- data/lib/couchproxy/rack/design_doc.rb +227 -0
- data/lib/couchproxy/rack/doc.rb +15 -0
- data/lib/couchproxy/rack/ensure_full_commit.rb +16 -0
- data/lib/couchproxy/rack/not_found.rb +13 -0
- data/lib/couchproxy/rack/replicate.rb +9 -0
- data/lib/couchproxy/rack/revs_limit.rb +18 -0
- data/lib/couchproxy/rack/root.rb +10 -0
- data/lib/couchproxy/rack/stats.rb +53 -0
- data/lib/couchproxy/rack/temp_view.rb +9 -0
- data/lib/couchproxy/rack/update.rb +11 -0
- data/lib/couchproxy/rack/users.rb +9 -0
- data/lib/couchproxy/rack/uuids.rb +9 -0
- data/lib/couchproxy/rack/view_cleanup.rb +16 -0
- data/lib/couchproxy/reducer.rb +57 -0
- data/lib/couchproxy/request.rb +50 -0
- data/lib/couchproxy/router.rb +62 -0
- data/lib/couchproxy.rb +48 -0
- data/lib/couchproxy.ru +22 -0
- data/test/collator_test.rb +100 -0
- metadata +164 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module CouchProxy
|
4
|
+
class Router
|
5
|
+
DB_NAME = '[a-z]([a-z0-9_$()+-]|%2[489bBfF])*'.freeze
|
6
|
+
ROOT = ''.freeze
|
7
|
+
UUIDS = '_uuids'.freeze
|
8
|
+
USERS = '_users'.freeze
|
9
|
+
STATS = '_stats'.freeze
|
10
|
+
REPLICATE = '_replicate'.freeze
|
11
|
+
ALL_DBS = '_all_dbs'.freeze
|
12
|
+
ACTIVE_TASKS = '_active_tasks'.freeze
|
13
|
+
CONFIG = /^(_config|_config\/.*)$/
|
14
|
+
BULK_DOCS = /^#{DB_NAME}\/_bulk_docs$/
|
15
|
+
ALL_DOCS = /^#{DB_NAME}\/_all_docs$/
|
16
|
+
DESIGN_DOC = /^#{DB_NAME}\/_design\/.+$/
|
17
|
+
COMPACT = /^#{DB_NAME}\/(_compact|_compact\/.*)$/
|
18
|
+
VIEW_CLEANUP = /^#{DB_NAME}\/_view_cleanup$/
|
19
|
+
TEMP_VIEW = /^#{DB_NAME}\/_temp_view$/
|
20
|
+
UPDATE = /^#{DB_NAME}\/_update$/
|
21
|
+
REVS_LIMIT = /^#{DB_NAME}\/_revs_limit$/
|
22
|
+
CHANGES = /^#{DB_NAME}\/_changes$/
|
23
|
+
FULL_COMMIT = /^#{DB_NAME}\/_ensure_full_commit$/
|
24
|
+
DATABASE = /^#{DB_NAME}$/
|
25
|
+
DOC = /^#{DB_NAME}\/.+$/
|
26
|
+
|
27
|
+
def initialize(cluster)
|
28
|
+
@cluster = cluster
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(env)
|
32
|
+
request = ::Rack::Request.new(env)
|
33
|
+
app = case env['REQUEST_PATH'][1..-1].chomp('/')
|
34
|
+
when ROOT then :root
|
35
|
+
when UUIDS then :uuids
|
36
|
+
when CONFIG then :config
|
37
|
+
when USERS then :users
|
38
|
+
when STATS then :stats
|
39
|
+
when REPLICATE then :replicate
|
40
|
+
when ALL_DBS then :all_databases
|
41
|
+
when ACTIVE_TASKS then :active_tasks
|
42
|
+
when BULK_DOCS then :bulk_docs
|
43
|
+
when ALL_DOCS then :all_docs
|
44
|
+
when DESIGN_DOC then :design_doc
|
45
|
+
when COMPACT then :compact
|
46
|
+
when VIEW_CLEANUP then :view_cleanup
|
47
|
+
when TEMP_VIEW then :temp_view
|
48
|
+
when UPDATE then :update
|
49
|
+
when REVS_LIMIT then :revs_limit
|
50
|
+
when CHANGES then :changes
|
51
|
+
when FULL_COMMIT then :ensure_full_commit
|
52
|
+
when DATABASE then :database
|
53
|
+
when DOC then :doc
|
54
|
+
else :not_found
|
55
|
+
end
|
56
|
+
name = app.to_s.split('_').map {|n| n.capitalize }.join
|
57
|
+
app = CouchProxy::Rack.const_get(name).new(request, @cluster)
|
58
|
+
app.send(request.request_method.downcase)
|
59
|
+
throw :async
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/couchproxy.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) unless
|
4
|
+
$:.include?(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
%w[
|
7
|
+
em-http
|
8
|
+
json
|
9
|
+
json/stream
|
10
|
+
thin
|
11
|
+
time
|
12
|
+
uri
|
13
|
+
yaml
|
14
|
+
zlib
|
15
|
+
|
16
|
+
couchproxy/collator
|
17
|
+
couchproxy/cluster
|
18
|
+
couchproxy/node
|
19
|
+
couchproxy/partition
|
20
|
+
couchproxy/deferrable_body
|
21
|
+
couchproxy/reducer
|
22
|
+
couchproxy/request
|
23
|
+
couchproxy/router
|
24
|
+
|
25
|
+
couchproxy/rack/base
|
26
|
+
couchproxy/rack/all_databases
|
27
|
+
couchproxy/rack/bulk_docs
|
28
|
+
couchproxy/rack/changes
|
29
|
+
couchproxy/rack/compact
|
30
|
+
couchproxy/rack/config
|
31
|
+
couchproxy/rack/database
|
32
|
+
couchproxy/rack/design_doc
|
33
|
+
couchproxy/rack/doc
|
34
|
+
couchproxy/rack/ensure_full_commit
|
35
|
+
couchproxy/rack/not_found
|
36
|
+
couchproxy/rack/replicate
|
37
|
+
couchproxy/rack/revs_limit
|
38
|
+
couchproxy/rack/root
|
39
|
+
couchproxy/rack/stats
|
40
|
+
couchproxy/rack/update
|
41
|
+
couchproxy/rack/users
|
42
|
+
couchproxy/rack/uuids
|
43
|
+
couchproxy/rack/view_cleanup
|
44
|
+
].each {|f| require f }
|
45
|
+
|
46
|
+
module CouchProxy
|
47
|
+
VERSION = '0.1.0'
|
48
|
+
end
|
data/lib/couchproxy.ru
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__) unless
|
2
|
+
$:.include?(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'couchproxy'
|
5
|
+
|
6
|
+
def cluster
|
7
|
+
yaml = ENV['COUCH_PROXY_CONFIG'] || ''
|
8
|
+
unless File.exist?(yaml)
|
9
|
+
raise ArgumentError.new('COUCH_PROXY_CONFIG must point to a couchproxy.yml file')
|
10
|
+
end
|
11
|
+
config = YAML.load_file(yaml)
|
12
|
+
raise ArgumentError.new('must define node list') unless config['nodes']
|
13
|
+
nodes = config['nodes'].map {|n| CouchProxy::Node.new(n['host'], n['partitions']) }
|
14
|
+
reducers = config['reducers'] || 4
|
15
|
+
couchjs = config['couchjs'] or raise ArgumentError.new('must define couchjs')
|
16
|
+
raise ArgumentError.new("#{couchjs} must be executable") unless File.executable?(couchjs)
|
17
|
+
mainjs = File.expand_path('../../share/couchdb/server/main.js', couchjs)
|
18
|
+
raise ArgumentError.new("could not find #{mainjs}") unless File.exist?(mainjs)
|
19
|
+
CouchProxy::Cluster.new(nodes, couchjs, reducers)
|
20
|
+
end
|
21
|
+
|
22
|
+
run CouchProxy::Router.new(cluster)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'couchproxy'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
# Test that we properly sort JSON keys according to
|
7
|
+
# http://wiki.apache.org/couchdb/View_collation.
|
8
|
+
class CollatorTest < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@collator = CouchProxy::Collator.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_keyword
|
14
|
+
assert_equal(0, @collator.compare(nil, nil))
|
15
|
+
assert_equal(0, @collator.compare(true, true))
|
16
|
+
assert_equal(0, @collator.compare(false, false))
|
17
|
+
|
18
|
+
assert_equal(-1, @collator.compare(nil, false))
|
19
|
+
assert_equal(1, @collator.compare(false, nil))
|
20
|
+
|
21
|
+
assert_equal(-1, @collator.compare(nil, true))
|
22
|
+
assert_equal(1, @collator.compare(true, nil))
|
23
|
+
|
24
|
+
assert_equal(-1, @collator.compare(false, true))
|
25
|
+
assert_equal(1, @collator.compare(true, false))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_number
|
29
|
+
assert_equal(0, @collator.compare(0, 0))
|
30
|
+
assert_equal(0, @collator.compare(0, 0.0))
|
31
|
+
assert_equal(0, @collator.compare(1, 1.0))
|
32
|
+
assert_equal(-1, @collator.compare(0, 1.0))
|
33
|
+
assert_equal(1, @collator.compare(1.0, 0))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_string
|
37
|
+
assert_equal(0, @collator.compare('', ''))
|
38
|
+
assert_equal(0, @collator.compare('a', 'a'))
|
39
|
+
assert_equal(-1, @collator.compare('a', 'aa'))
|
40
|
+
assert_equal(-1, @collator.compare('a', 'b'))
|
41
|
+
assert_equal(1, @collator.compare('b', 'a'))
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_type
|
45
|
+
assert_equal(-1, @collator.compare(nil, 0))
|
46
|
+
assert_equal(-1, @collator.compare(nil, ''))
|
47
|
+
assert_equal(-1, @collator.compare(nil, []))
|
48
|
+
assert_equal(-1, @collator.compare(nil, {}))
|
49
|
+
|
50
|
+
assert_equal(-1, @collator.compare(true, 0))
|
51
|
+
assert_equal(-1, @collator.compare(true, ''))
|
52
|
+
assert_equal(-1, @collator.compare(true, []))
|
53
|
+
assert_equal(-1, @collator.compare(true, {}))
|
54
|
+
|
55
|
+
assert_equal(-1, @collator.compare(false, 0))
|
56
|
+
assert_equal(-1, @collator.compare(false, ''))
|
57
|
+
assert_equal(-1, @collator.compare(false, []))
|
58
|
+
assert_equal(-1, @collator.compare(false, {}))
|
59
|
+
|
60
|
+
assert_equal(-1, @collator.compare(0, ''))
|
61
|
+
assert_equal(-1, @collator.compare(0, []))
|
62
|
+
assert_equal(-1, @collator.compare(0, {}))
|
63
|
+
|
64
|
+
assert_equal(-1, @collator.compare('', []))
|
65
|
+
assert_equal(-1, @collator.compare('', {}))
|
66
|
+
|
67
|
+
assert_equal(-1, @collator.compare([], {}))
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_array
|
71
|
+
assert_equal(0, @collator.compare([], []))
|
72
|
+
assert_equal(0, @collator.compare([0], [0]))
|
73
|
+
assert_equal(0, @collator.compare([0], [0.0]))
|
74
|
+
|
75
|
+
assert_equal(-1, @collator.compare([], [0]))
|
76
|
+
assert_equal(-1, @collator.compare([0], [0, 1]))
|
77
|
+
assert_equal(1, @collator.compare([0], []))
|
78
|
+
assert_equal(1, @collator.compare([0, 1], [0]))
|
79
|
+
|
80
|
+
assert_equal(1, @collator.compare([0], [-1, 0]))
|
81
|
+
assert_equal(-1, @collator.compare([-1, 0], [0]))
|
82
|
+
|
83
|
+
assert_equal(-1, @collator.compare([nil], [false]))
|
84
|
+
assert_equal(-1, @collator.compare([nil], [nil, false]))
|
85
|
+
|
86
|
+
assert_equal(0, @collator.compare([[]], [[]]))
|
87
|
+
assert_equal(-1, @collator.compare([[0]], [[1]]))
|
88
|
+
assert_equal(-1, @collator.compare([0], [[]]))
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_hash
|
92
|
+
assert_equal(0, @collator.compare({}, {}))
|
93
|
+
assert_equal(0, @collator.compare({nil => nil}, {nil => nil}))
|
94
|
+
assert_equal(-1, @collator.compare({nil => nil}, {nil => true}))
|
95
|
+
assert_equal(1, @collator.compare({nil => true}, {nil => nil}))
|
96
|
+
assert_equal(-1, @collator.compare({'a' => 1}, {'a' => 2}))
|
97
|
+
assert_equal(-1, @collator.compare({'b' => 2}, {'b' => 2, 'a' => 1}))
|
98
|
+
assert_equal(-1, @collator.compare({'b' => 2, 'a' => 1}, {'b' => 2, 'c' => 2}))
|
99
|
+
end
|
100
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: couchproxy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- David Graham
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-09-06 00:00:00 -06:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: em-http-request
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 2
|
31
|
+
version: "0.2"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: json
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 1
|
44
|
+
- 4
|
45
|
+
version: "1.4"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: json-stream
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
- 1
|
59
|
+
version: "0.1"
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: thin
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ~>
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
segments:
|
71
|
+
- 1
|
72
|
+
- 2
|
73
|
+
version: "1.2"
|
74
|
+
type: :runtime
|
75
|
+
version_requirements: *id004
|
76
|
+
description: |-
|
77
|
+
CouchProxy is a simple proxy server that distributes reads and writes to a
|
78
|
+
cluster of Apache CouchDB servers so they appear to be a single huge database.
|
79
|
+
Documents are stored and retrieved from a particular CouchDB instance, using
|
80
|
+
consistent hashing of the document id. Map/reduce views are processed
|
81
|
+
concurrently on each CouchDB instance and merged together by the proxy before
|
82
|
+
returning the results to the client.
|
83
|
+
email: david.malcom.graham@gmail.com
|
84
|
+
executables:
|
85
|
+
- couchproxy
|
86
|
+
extensions: []
|
87
|
+
|
88
|
+
extra_rdoc_files: []
|
89
|
+
|
90
|
+
files:
|
91
|
+
- LICENSE
|
92
|
+
- Rakefile
|
93
|
+
- README
|
94
|
+
- bin/couchproxy
|
95
|
+
- lib/couchproxy/cluster.rb
|
96
|
+
- lib/couchproxy/collator.rb
|
97
|
+
- lib/couchproxy/deferrable_body.rb
|
98
|
+
- lib/couchproxy/node.rb
|
99
|
+
- lib/couchproxy/partition.rb
|
100
|
+
- lib/couchproxy/rack/active_tasks.rb
|
101
|
+
- lib/couchproxy/rack/all_databases.rb
|
102
|
+
- lib/couchproxy/rack/all_docs.rb
|
103
|
+
- lib/couchproxy/rack/base.rb
|
104
|
+
- lib/couchproxy/rack/bulk_docs.rb
|
105
|
+
- lib/couchproxy/rack/changes.rb
|
106
|
+
- lib/couchproxy/rack/compact.rb
|
107
|
+
- lib/couchproxy/rack/config.rb
|
108
|
+
- lib/couchproxy/rack/database.rb
|
109
|
+
- lib/couchproxy/rack/design_doc.rb
|
110
|
+
- lib/couchproxy/rack/doc.rb
|
111
|
+
- lib/couchproxy/rack/ensure_full_commit.rb
|
112
|
+
- lib/couchproxy/rack/not_found.rb
|
113
|
+
- lib/couchproxy/rack/replicate.rb
|
114
|
+
- lib/couchproxy/rack/revs_limit.rb
|
115
|
+
- lib/couchproxy/rack/root.rb
|
116
|
+
- lib/couchproxy/rack/stats.rb
|
117
|
+
- lib/couchproxy/rack/temp_view.rb
|
118
|
+
- lib/couchproxy/rack/update.rb
|
119
|
+
- lib/couchproxy/rack/users.rb
|
120
|
+
- lib/couchproxy/rack/uuids.rb
|
121
|
+
- lib/couchproxy/rack/view_cleanup.rb
|
122
|
+
- lib/couchproxy/reducer.rb
|
123
|
+
- lib/couchproxy/request.rb
|
124
|
+
- lib/couchproxy/router.rb
|
125
|
+
- lib/couchproxy.rb
|
126
|
+
- lib/couchproxy.ru
|
127
|
+
- conf/couchproxy.yml
|
128
|
+
- test/collator_test.rb
|
129
|
+
has_rdoc: true
|
130
|
+
homepage: http://github.com/dgraham/couchproxy
|
131
|
+
licenses: []
|
132
|
+
|
133
|
+
post_install_message:
|
134
|
+
rdoc_options: []
|
135
|
+
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
segments:
|
144
|
+
- 1
|
145
|
+
- 9
|
146
|
+
- 1
|
147
|
+
version: 1.9.1
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
version: "0"
|
156
|
+
requirements: []
|
157
|
+
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 1.3.7
|
160
|
+
signing_key:
|
161
|
+
specification_version: 3
|
162
|
+
summary: A proxy server for Apache CouchDB clusters.
|
163
|
+
test_files:
|
164
|
+
- test/collator_test.rb
|