execache 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,6 +1,7 @@
1
1
  .DS_Store
2
2
  *.gem
3
3
  .bundle
4
+ dump.rdb
4
5
  Gemfile.lock
5
6
  pkg
6
7
  tmp
data/execache.gemspec CHANGED
@@ -6,7 +6,7 @@ $:.unshift lib unless $:.include?(lib)
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "execache"
9
- s.version = '0.1.1'
9
+ s.version = '0.1.2'
10
10
  s.platform = Gem::Platform::RUBY
11
11
  s.authors = [ "Winton Welsh" ]
12
12
  s.email = [ "mail@wintoni.us" ]
@@ -9,18 +9,29 @@ class Execache
9
9
  end
10
10
 
11
11
  def exec(options)
12
- options[:channel] = Digest::SHA1.hexdigest("#{rand}")
12
+ wait = options.delete(:wait)
13
+ subscribe_to = options[:channel] = Digest::SHA1.hexdigest("#{rand}")
14
+ options = Yajl::Encoder.encode(options)
13
15
  response = nil
14
16
 
15
17
  Timeout.timeout(60) do
16
- @redis_1.subscribe("execache:response:#{options[:channel]}") do |on|
18
+ @redis_1.subscribe("execache:response:#{subscribe_to}") do |on|
17
19
  on.subscribe do |channel, subscriptions|
18
- @redis_2.rpush "execache:request", Yajl::Encoder.encode(options)
20
+ @redis_2.rpush "execache:request", options
19
21
  end
20
22
 
21
23
  on.message do |channel, message|
22
- response = Yajl::Parser.parse(message)
23
- @redis_1.unsubscribe
24
+ if message.include?('[PENDING]')
25
+ if wait == false
26
+ response = false
27
+ @redis_1.unsubscribe
28
+ else
29
+ @redis_2.rpush "execache:request", options
30
+ end
31
+ else
32
+ response = Yajl::Parser.parse(message)
33
+ @redis_1.unsubscribe
34
+ end
24
35
  end
25
36
  end
26
37
  end
data/lib/execache.rb CHANGED
@@ -26,10 +26,11 @@ class Execache
26
26
  while true
27
27
  request = redis.lpop('execache:request')
28
28
  if request
29
- Thread.new do
29
+ Timeout.timeout(60) do
30
30
  request = Yajl::Parser.parse(request)
31
31
  channel = request.delete('channel')
32
32
  commands = []
33
+ pending = false
33
34
 
34
35
  request.each do |cmd_type, cmd_options|
35
36
  # Command with preliminary args
@@ -46,11 +47,14 @@ class Execache
46
47
  group['cache_key'] = cache_key = "execache:cache:#{cache_key}"
47
48
  cache = redis.get(cache_key)
48
49
 
49
- if cache
50
+ if cache && cache == '[PENDING]'
51
+ pending = true
52
+ elsif cache
50
53
  group['result'] = Yajl::Parser.parse(cache)
51
54
  else
55
+ pending = true
56
+ redis.set(cache_key, '[PENDING]')
52
57
  command << group['args']
53
- nil
54
58
  end
55
59
  end
56
60
 
@@ -60,40 +64,49 @@ class Execache
60
64
  end
61
65
  end
62
66
 
63
- # Build response
64
- response = request.inject({}) do |hash, (cmd_type, cmd_options)|
65
- hash[cmd_type] = []
67
+ if pending
68
+ # Execute command in thread, cache results
69
+ Thread.new do
70
+ Timeout.timeout(60) do
71
+ request.each do |cmd_type, cmd_options|
72
+ if cmd_options['cmd']
73
+ separators = options[cmd_type]['separators'] || {}
74
+ separators['group'] ||= "[END]"
75
+ separators['result'] ||= "\n"
76
+ output = `#{cmd_options['cmd']}`
77
+ output = output.split(separators['group'] + separators['result'])
78
+ output = output.collect { |r| r.split(separators['result']) }
79
+ end
66
80
 
67
- if cmd_options['cmd']
68
- separators = options[cmd_type]['separators'] || {}
69
- separators['group'] ||= "[END]"
70
- separators['result'] ||= "\n"
71
- output = `#{cmd_options['cmd']}`
72
- output = output.split(separators['group'] + separators['result'])
73
- output = output.collect { |r| r.split(separators['result']) }
81
+ cmd_options['groups'].each do |group|
82
+ unless group['result']
83
+ redis.set(
84
+ group['cache_key'],
85
+ Yajl::Encoder.encode(output.shift)
86
+ )
87
+ if group['ttl']
88
+ redis.expire(group['cache_key'], group['ttl'])
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
74
94
  end
95
+ else
96
+ response = request.inject({}) do |hash, (cmd_type, cmd_options)|
97
+ hash[cmd_type] = []
75
98
 
76
- cmd_options['groups'].each do |group|
77
- if group['result']
99
+ cmd_options['groups'].each do |group|
78
100
  hash[cmd_type] << group['result']
79
- else
80
- hash[cmd_type] << output.shift
81
- redis.set(
82
- group['cache_key'],
83
- Yajl::Encoder.encode(hash[cmd_type].last)
84
- )
85
- if group['ttl']
86
- redis.expire(group['cache_key'], group['ttl'])
87
- end
88
101
  end
89
- end
90
102
 
91
- hash
103
+ hash
104
+ end
92
105
  end
93
106
 
94
107
  redis.publish(
95
108
  "execache:response:#{channel}",
96
- Yajl::Encoder.encode(response)
109
+ pending ? '[PENDING]' : Yajl::Encoder.encode(response)
97
110
  )
98
111
  end
99
112
  end
@@ -2,21 +2,23 @@ require 'spec_helper'
2
2
 
3
3
  describe Execache do
4
4
 
5
- def client_exec
5
+ def client_exec(options={})
6
6
  @client.exec(
7
- :some_binary => {
8
- :args => 'preliminary_arg',
9
- :groups => [
10
- {
11
- :args => 'arg1a arg1b',
12
- :ttl => 60
13
- },
14
- {
15
- :args => 'arg2a arg2b',
16
- :ttl => 60
17
- }
18
- ]
19
- }
7
+ {
8
+ :some_binary => {
9
+ :args => 'preliminary_arg',
10
+ :groups => [
11
+ {
12
+ :args => 'arg1a arg1b',
13
+ :ttl => 60
14
+ },
15
+ {
16
+ :args => 'arg2a arg2b',
17
+ :ttl => 60
18
+ }
19
+ ]
20
+ }
21
+ }.merge(options)
20
22
  )
21
23
  end
22
24
 
@@ -120,4 +122,17 @@ describe Execache do
120
122
  ]
121
123
  }
122
124
  end
125
+
126
+ it "should respect wait option" do
127
+ @client.redis_1.keys("execache:cache:*").each do |key|
128
+ @client.redis_1.del(key)
129
+ end
130
+ client_exec(:wait => false).should == false
131
+ client_exec.should == {
132
+ "some_binary" => [
133
+ ["arg1_result_1", "arg1_result_2"],
134
+ ["arg2_result_1", "arg2_result_2"]
135
+ ]
136
+ }
137
+ end
123
138
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: execache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-11 00:00:00.000000000Z
12
+ date: 2011-10-14 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70277360546840 !ruby/object:Gem::Requirement
16
+ requirement: &70246699607900 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70277360546840
24
+ version_requirements: *70246699607900
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: redis
27
- requirement: &70277360546360 !ruby/object:Gem::Requirement
27
+ requirement: &70246699607420 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 2.2.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70277360546360
35
+ version_requirements: *70246699607420
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: yajl-ruby
38
- requirement: &70277360545900 !ruby/object:Gem::Requirement
38
+ requirement: &70246699606960 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: 1.0.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70277360545900
46
+ version_requirements: *70246699606960
47
47
  description: Run commands in parallel and cache the output. Redis queues jobs and
48
48
  stores the result.
49
49
  email: