couchproxy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|