infopark_cloud_connector 6.8.2.36.82613853 → 6.8.3.1.23895778

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,8 @@
1
1
  module RailsConnector
2
2
 
3
3
  class Cache
4
+ attr_reader :cache_prefix
5
+
4
6
  def initialize(options = {})
5
7
  @fallback_backend, @cache_prefix = options[:fallback_backend], options[:cache_prefix]
6
8
  @cache = {}
@@ -24,6 +26,10 @@ class Cache
24
26
  read(key) || block.call.tap { |value| write(key, value) }
25
27
  end
26
28
 
29
+ def clear
30
+ @cache = {}
31
+ end
32
+
27
33
  private
28
34
 
29
35
  def cache_key_for_fallback_backend(key)
@@ -25,7 +25,7 @@ module RailsConnector
25
25
  end
26
26
  end
27
27
 
28
- def query_index(model, index, params = {})
28
+ def query_index(model, index, params = {}, query_options={})
29
29
  hash_value = "#{model}/#{index}/#{params[:hash_value]}"
30
30
  range_value = params[:range_value] || ""
31
31
 
@@ -41,7 +41,7 @@ module RailsConnector
41
41
  end
42
42
  range_value = Range.new(start_key, end_key + "~")
43
43
 
44
- adapter.query(hash_value, range_value)
44
+ adapter.query(hash_value, range_value, query_options)
45
45
  end
46
46
 
47
47
  end
@@ -0,0 +1,44 @@
1
+ module RailsConnector
2
+
3
+ # This module manages storage of different kind of cache data.
4
+ module CmsCacheStorage
5
+ VERSION = 'v1'.freeze
6
+
7
+ class << self
8
+ attr_writer :cache
9
+
10
+ def cache
11
+ @cache ||= begin
12
+ prefix = VERSION.dup
13
+ prefix.concat('-utf8') if String.new.encoding_aware?
14
+ Cache.new(fallback_backend: Rails.cache, cache_prefix: prefix)
15
+ end
16
+ end
17
+
18
+ def read_workspace_content_state_id(workspace_id)
19
+ cache.read("workspace/#{workspace_id}/content_state")
20
+ end
21
+
22
+ def write_workspace_content_state_id(workspace_id, content_state_id)
23
+ cache.write("workspace/#{workspace_id}/content_state", content_state_id)
24
+ end
25
+
26
+ def read_content_state(content_state_id)
27
+ cache.read("content/#{content_state_id}")
28
+ end
29
+
30
+ def write_content_state(content_state_id, content_state)
31
+ cache.write("content/#{content_state_id}", content_state)
32
+ end
33
+
34
+ def read_obj_data(content_state_id, index, key)
35
+ cache.read("content/#{content_state_id}/obj/#{index}/#{key}")
36
+ end
37
+
38
+ def write_obj_data(content_state_id, index, key, data)
39
+ cache.write("content/#{content_state_id}/obj/#{index}/#{key}", data)
40
+ end
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,109 @@
1
+ module RailsConnector
2
+
3
+ # This class represents a single instance of a content state.
4
+ # Basically a content state has three important characteristics: cache with obj datas,
5
+ # changes representing changed objs and ways they has been changed and an ancestor content state.
6
+ class ContentState < Struct.new(:content_state_id, :changes, :changes_index, :from_content_state_id)
7
+ class << self
8
+ private :new
9
+
10
+ # Creates a new content state with given changes and ancestor (optional).
11
+ def create(attributes)
12
+ new(attributes).tap do |content_state|
13
+ content_state.index_changes!
14
+ CmsCacheStorage.write_content_state(content_state.content_state_id, content_state.to_hash)
15
+ end
16
+ end
17
+
18
+ # Finds a previously saved content state.
19
+ # Returns nil if not found.
20
+ def find(content_state_id)
21
+ if content_state_data = CmsCacheStorage.read_content_state(content_state_id)
22
+ new(content_state_data)
23
+ end
24
+ end
25
+
26
+ # Fetches an existing workspace.
27
+ # If not found creates a new one and returns it.
28
+ def find_or_create(content_state_id)
29
+ find(content_state_id) || create(content_state_id: content_state_id)
30
+ end
31
+ end
32
+
33
+ def initialize(attributes)
34
+ super(*attributes.symbolize_keys.values_at(:content_state_id, :changes, :changes_index,
35
+ :from_content_state_id))
36
+ end
37
+
38
+ # Stores arbitrary data in cache.
39
+ # Cache key is build from given index and key.
40
+ def save_obj_data(index, key, data)
41
+ CmsCacheStorage.write_obj_data(content_state_id, index, key, data)
42
+ end
43
+
44
+ # Fetches previously stored arbitrary data from cache.
45
+ # Returns nil if nothing found.
46
+ # Cache key is build from given index and key.
47
+ def find_obj_data(index, key)
48
+ CmsCacheStorage.read_obj_data(content_state_id, index, key)
49
+ end
50
+
51
+ # Fetches and caches the ancestor.
52
+ # Returns nil if there is no ancestor.
53
+ def from_content_state
54
+ @from_content_state ||= self.class.find(from_content_state_id)
55
+ end
56
+
57
+ # Determines whether given data is still up-to-date for given index and key.
58
+ def has_changes_for?(index, key, data)
59
+ case index
60
+ when 'id'
61
+ id_index.include?(key)
62
+ when 'path'
63
+ path_index.include?(key) || id_index.include?(data.first['_id'].first)
64
+ when 'ppath'
65
+ ppath_index.include?(key) || data.find { |d| id_index.include?(d['_id'].first) }
66
+ end
67
+ end
68
+
69
+ # Computes for a given changes feed a set of access-efficient indexes.
70
+ def index_changes!
71
+ id_index, path_index, ppath_index = Set.new, Set.new, Set.new
72
+ if changes.present?
73
+ changes.each do |hash|
74
+ id_index.add(hash['id'])
75
+ if path = hash['modified_path']
76
+ path_index.add(path)
77
+ ppath_index.add(path.gsub(/\/[^\/]+$/, '').presence || '/') if path != '/'
78
+ end
79
+ end
80
+ end
81
+ self.changes = nil
82
+ self.changes_index = {'id' => id_index, 'path' => path_index, 'ppath' => ppath_index}
83
+ end
84
+
85
+ # Returns a hash representation of a content state for serialization purpose.
86
+ def to_hash
87
+ {
88
+ content_state_id: content_state_id,
89
+ changes_index: changes_index,
90
+ from_content_state_id: from_content_state_id
91
+ }
92
+ end
93
+
94
+ private
95
+
96
+ def id_index
97
+ @id_index ||= changes_index['id']
98
+ end
99
+
100
+ def path_index
101
+ @path_index ||= changes_index['path']
102
+ end
103
+
104
+ def ppath_index
105
+ @ppath_index ||= changes_index['ppath']
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,67 @@
1
+ module RailsConnector
2
+
3
+ # This module provides advances auto-invalidating caching mechanism for storing obj data.
4
+ #
5
+ # To keep it up-to-date it's caches and changes should be updated periodically
6
+ # It's changes should be updated every time a new workspace data has been fetched.
7
+ # It's caches should be updated every time a new obj data has been fetched.
8
+ module ContentStateCaching
9
+ class << self
10
+ # How deep should a content state chain be inspected. Default depth is 20.
11
+ attr_accessor :cache_lookup_depth
12
+
13
+ # At which lookup depth to copy a hit found in an ancestor content state
14
+ # to the current content state's cache. Default depth is 5.
15
+ attr_accessor :cache_replication_depth
16
+
17
+ # Creates a new content state for given workspace with given ancetor and returns it.
18
+ def store_content_state(workspace_data)
19
+ ContentState.create(content_state_id: workspace_data.to_content_state_id,
20
+ changes: workspace_data.changes,
21
+ from_content_state_id: workspace_data.from_content_state_id)
22
+ end
23
+
24
+ # Fetches last known content state id for a given workspace id.
25
+ # Returns nil if there is no content state for that workspace.
26
+ def find_content_state_id(workspace_id)
27
+ CmsCacheStorage.read_workspace_content_state_id(workspace_id)
28
+ end
29
+
30
+ # Stores current content state id for workspace with given id.
31
+ def store_content_state_id(workspace_id, content_state_id)
32
+ CmsCacheStorage.write_workspace_content_state_id(workspace_id, content_state_id)
33
+ end
34
+
35
+ # Updates caches with data from given workspace.
36
+ # Should be called every time a new OBJ data has been fetched.
37
+ def store_obj_data(workspace_data, index, key, data)
38
+ content_state = workspace_data.content_state
39
+ content_state.save_obj_data(index, key, data) if content_state
40
+ end
41
+
42
+ # Fetches up-to-date obj data for given workspace, index and key.
43
+ # Returns nil if no up-to-date data found.
44
+ def find_obj_data(workspace_data, index, key)
45
+ return unless current_content_state = workspace_data.content_state
46
+ if index == 'permalink'
47
+ current_content_state.find_obj_data(index, key)
48
+ else
49
+ visitor = ContentStateVisitor.new(current_content_state)
50
+ cache_lookup_depth.times do |depth|
51
+ return unless content_state = visitor.visit_next
52
+ if hit = content_state.find_obj_data(index, key)
53
+ visitor.visited_except_current.each { |cs| return if cs.has_changes_for?(index, key, hit) }
54
+ current_content_state.save_obj_data(index, key, hit) if depth >= cache_replication_depth
55
+ return hit
56
+ end
57
+ end
58
+ nil
59
+ end
60
+ end
61
+ end
62
+
63
+ self.cache_replication_depth = 5
64
+ self.cache_lookup_depth = 20
65
+ end
66
+
67
+ end
@@ -0,0 +1,19 @@
1
+ module RailsConnector
2
+ class ContentStateVisitor
3
+ def initialize(start_content_state)
4
+ @next_content_state, @visited = start_content_state, []
5
+ end
6
+
7
+ def visit_next
8
+ if content_state = @next_content_state
9
+ @visited << content_state
10
+ @next_content_state = content_state.from_content_state
11
+ content_state
12
+ end
13
+ end
14
+
15
+ def visited_except_current
16
+ @visited[0..-2]
17
+ end
18
+ end
19
+ end
@@ -56,7 +56,7 @@ module RailsConnector
56
56
  @query_counter += 1
57
57
  instrumenter = ActiveSupport::Notifications.instrumenter
58
58
  instrumenter.instrument(
59
- "cms_load.rails_connector", :name => "Obj Load", :index => index, :keys => keys
59
+ "cms_load.rails_connector", :name => "Obj Load", :index => index.to_s, :keys => keys
60
60
  ) do
61
61
  keys.map do |key|
62
62
  results = revision.chain.query(index, key)
@@ -58,10 +58,18 @@ module RailsConnector
58
58
  rtc['attributes']
59
59
  end
60
60
 
61
+ def rtc_id
62
+ rtc_ref['id']
63
+ end
64
+
61
65
  private
62
66
 
63
67
  def rtc
64
- @rtc ||= DictStorage.get(read_attribute("rtc_ref"))
68
+ @rtc ||= DictStorage.get(rtc_ref)
69
+ end
70
+
71
+ def rtc_ref
72
+ read_attribute('rtc_ref')
65
73
  end
66
74
  end
67
75
  end
@@ -48,25 +48,24 @@ module RailsConnector
48
48
 
49
49
  # connects the cloud connector to the connector service
50
50
  class ServiceCmsBackend < CmsBackend
51
- VERSION = "v1".freeze
52
51
  BLOB_DATA_CACHE_PREFIX = 'blob_data'.freeze
52
+ VALID_INDEX_NAMES = %w[id path ppath permalink].freeze
53
53
 
54
54
  def initialize
55
55
  @query_counter = 0
56
56
  end
57
57
 
58
58
  def begin_caching
59
- @editable_cache = Configuration.cache_editable_workspaces ? persistent_cache : Cache.new
60
- @read_only_cache = persistent_cache
61
- @blob_data_cache = persistent_cache
59
+ @caching = true
62
60
  end
63
61
 
64
62
  def end_caching
65
- @editable_cache = @read_only_cache = @blob_data_cache = nil
63
+ CmsCacheStorage.cache.clear
64
+ @caching = false
66
65
  end
67
66
 
68
67
  def caching?
69
- @read_only_cache && @editable_cache && @blob_data_cache
68
+ !!@caching
70
69
  end
71
70
 
72
71
  def query_counter
@@ -74,23 +73,25 @@ module RailsConnector
74
73
  end
75
74
 
76
75
  def find_workspace_data_by_id(id)
77
- raw_data = ContentService.query(
78
- "workspaces/query",
79
- :workspace_id => id
80
- )
76
+ from_content_state_id = ContentStateCaching.find_content_state_id(id)
77
+ request_params = {:workspace_id => id}
78
+ request_params[:content_state_id] = from_content_state_id if from_content_state_id
79
+ raw_data = ContentService.query('workspaces/query', request_params)
81
80
  if raw_workspace_data = raw_data['workspace']
82
- WorkspaceDataFromService.new raw_workspace_data
81
+ workspace_data = WorkspaceDataFromService.new(raw_workspace_data)
82
+ if from_content_state_id != workspace_data.content_state_id
83
+ ContentStateCaching.store_content_state(workspace_data)
84
+ ContentStateCaching.store_content_state_id(workspace_data.id,
85
+ workspace_data.content_state_id)
86
+ end
87
+ workspace_data
83
88
  end
84
89
  end
85
90
 
86
91
  def find_obj_data_by(workspace_data, index, keys)
87
- raw_data =
88
- if caching?
89
- find_raw_data_from_cache_or_database_by(workspace_data, index, keys)
90
- else
91
- find_raw_data_from_database_by(workspace_data, index, keys)
92
- end
93
-
92
+ index = index.to_s
93
+ assert_valid_index_name(index)
94
+ raw_data = find_raw_data_from_cache_or_database_by(workspace_data, index, keys)
94
95
  raw_data.map do |raw_list|
95
96
  raw_list.map do |raw_obj_data|
96
97
  ObjDataFromService.new(raw_obj_data)
@@ -99,30 +100,25 @@ module RailsConnector
99
100
  end
100
101
 
101
102
  def find_blob_data_by_id(id)
102
- if caching?
103
- find_blob_data_from_cache_or_database_by_id(id)
103
+ cache_key = "#{BLOB_DATA_CACHE_PREFIX}/#{id}"
104
+ if data_from_cache = CmsCacheStorage.cache.read(cache_key)
105
+ data_from_cache
104
106
  else
105
- find_blob_data_from_database_by(id)
107
+ data_from_database = find_blob_data_from_database_by(id)
108
+ if maxage = data_from_database['maxage']
109
+ CmsCacheStorage.cache.write(cache_key, data_from_database, :expires_in => maxage)
110
+ end
111
+ data_from_database
106
112
  end
107
113
  end
108
114
 
109
115
  private
110
116
 
111
- def encoding_aware_cache_prefix
112
- prefix = VERSION.dup
113
- prefix.concat('-utf8') if String.new.encoding_aware?
114
- prefix
115
- end
116
-
117
- def persistent_cache
118
- Cache.new(:fallback_backend => Rails.cache, :cache_prefix => encoding_aware_cache_prefix)
119
- end
120
-
121
117
  def find_raw_data_from_cache_or_database_by(workspace_data, index, keys)
122
118
  keys_from_database = []
123
119
  # load results from cache
124
120
  results_from_cache = keys.map do |key|
125
- cache_for(workspace_data).read(cache_key_for(workspace_data, index, key)).tap do |objs|
121
+ find_raw_data_from_cache_by(workspace_data, index, key).tap do |objs|
126
122
  keys_from_database << key unless objs
127
123
  end
128
124
  end
@@ -140,6 +136,10 @@ module RailsConnector
140
136
  end
141
137
  end
142
138
 
139
+ def find_raw_data_from_cache_by(workspace_data, index, key)
140
+ ContentStateCaching.find_obj_data(workspace_data, index, key) if caching?
141
+ end
142
+
143
143
  def find_raw_data_from_database_by(workspace_data, index, keys)
144
144
  return [] if keys.blank?
145
145
  instrumenter = ActiveSupport::Notifications.instrumenter
@@ -177,28 +177,7 @@ module RailsConnector
177
177
  end
178
178
 
179
179
  def store_item_in_cache(workspace_data, index, key, item)
180
- cache_for(workspace_data).write(cache_key_for(workspace_data, index, key), item) if caching?
181
- end
182
-
183
- def cache_key_for(workspace_data, index, key)
184
- "rev/#{workspace_data.revision_id}/obj/#{index}/#{key}"
185
- end
186
-
187
- def cache_for(workspace_data)
188
- workspace_data.cachable? ? @read_only_cache : @editable_cache
189
- end
190
-
191
- def find_blob_data_from_cache_or_database_by_id(id)
192
- cache_key = "#{BLOB_DATA_CACHE_PREFIX}/#{id}"
193
- if data_from_cache = @blob_data_cache.read(cache_key)
194
- data_from_cache
195
- else
196
- data_from_database = find_blob_data_from_database_by(id)
197
- if maxage = data_from_database['maxage']
198
- @blob_data_cache.write(cache_key, data_from_database, :expires_in => maxage)
199
- end
200
- data_from_database
201
- end
180
+ ContentStateCaching.store_obj_data(workspace_data, index, key, item)
202
181
  end
203
182
 
204
183
  def find_blob_data_from_database_by(id)
@@ -206,6 +185,10 @@ module RailsConnector
206
185
  ContentService.query('blobs/query', :blob_ids => [id])['blobs'][id]
207
186
  end
208
187
 
188
+ def assert_valid_index_name(index)
189
+ raise ArgumentError, "invalid index name '#{index}'" unless VALID_INDEX_NAMES.include?(index)
190
+ end
191
+
209
192
  end
210
193
 
211
194
  end
@@ -13,14 +13,34 @@ class WorkspaceDataFromService
13
13
  @data["id"]
14
14
  end
15
15
 
16
- def cachable?
17
- @data["is_cachable"]
18
- end
19
-
20
16
  # remove this method after DynamoCmsBackend has been removed from the Cloud Connector
21
17
  def content_cache_id=(id)
22
18
  # ignore, since not using content caches
23
19
  end
20
+
21
+ def content_state_id
22
+ @data['content_state_id']
23
+ end
24
+
25
+ def content_state
26
+ @content_state ||= ContentState.find_or_create(content_state_id) if content_state_id
27
+ end
28
+
29
+ def diff
30
+ @data['diff']
31
+ end
32
+
33
+ def changes
34
+ diff && diff['changes']
35
+ end
36
+
37
+ def from_content_state_id
38
+ diff && diff['from_content_state_id']
39
+ end
40
+
41
+ def to_content_state_id
42
+ diff && diff['to_content_state_id']
43
+ end
24
44
  end
25
45
 
26
46
  end # module RailsConnector
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: infopark_cloud_connector
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.8.2.36.82613853
4
+ version: 6.8.3.1.23895778
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-31 00:00:00.000000000 Z
12
+ date: 2013-02-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: 6.8.2.36.82613853
53
+ version: 6.8.3.1.23895778
54
54
  type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: 6.8.2.36.82613853
61
+ version: 6.8.3.1.23895778
62
62
  description: The Cloud Connector for Infopark CMS Fiona enables you to develop modern,
63
63
  dynamic Web 2.0 applications using Ruby on Rails and at the same time display CMS
64
64
  managed content.
@@ -89,9 +89,13 @@ files:
89
89
  - lib/rails_connector/cms_api_search_request.rb
90
90
  - lib/rails_connector/cms_backend.rb
91
91
  - lib/rails_connector/cms_base_model.rb
92
+ - lib/rails_connector/cms_cache_storage.rb
92
93
  - lib/rails_connector/cms_rest_api.rb
93
94
  - lib/rails_connector/content_cache.rb
94
95
  - lib/rails_connector/content_service.rb
96
+ - lib/rails_connector/content_state.rb
97
+ - lib/rails_connector/content_state_caching.rb
98
+ - lib/rails_connector/content_state_visitor.rb
95
99
  - lib/rails_connector/controller_runtime.rb
96
100
  - lib/rails_connector/date_attribute.rb
97
101
  - lib/rails_connector/default_search_request.rb
@@ -142,7 +146,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
142
146
  version: '0'
143
147
  segments:
144
148
  - 0
145
- hash: -1009000627
149
+ hash: -805109069
146
150
  required_rubygems_version: !ruby/object:Gem::Requirement
147
151
  none: false
148
152
  requirements: