Skip to content

Commit a20a855

Browse files
committed
(GH-236) Add a caching layer to Puppet assets
Previously the Puppet assets were always retrieved directly from Puppet. This commit changes the asset loaders (functions, types etc.) to save their results into a central object cache. This essentially becomes a write-through cache engine.
1 parent 8366d21 commit a20a855

File tree

4 files changed

+364
-76
lines changed

4 files changed

+364
-76
lines changed

server/lib/puppet-languageserver.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ def self.init_puppet(options)
102102
log_message(:info, "Language Server is v#{PuppetVSCode.version}")
103103
log_message(:info, "Using Puppet v#{Puppet.version}")
104104

105+
log_message(:info, 'Initializing Puppet Helper Cache...')
106+
PuppetLanguageServer::PuppetHelper.configure_cache(options[:cache])
107+
105108
log_message(:info, 'Initializing settings...')
106109
if options[:fast_start_tcpserver]
107110
Thread.new do

server/lib/puppet-languageserver/puppet_helper.rb

Lines changed: 71 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
require 'puppet/indirector/face'
22
require 'pathname'
33

4-
%w[puppet_helper/faux_objects].each do |lib|
5-
begin
6-
require "puppet-languageserver/#{lib}"
7-
rescue LoadError
8-
require File.expand_path(File.join(File.dirname(__FILE__), lib))
9-
end
4+
%w[puppet_helper/faux_objects puppet_helper/cache].each do |lib|
5+
begin
6+
require "puppet-languageserver/#{lib}"
7+
rescue LoadError
8+
require File.expand_path(File.join(File.dirname(__FILE__), lib))
9+
end
1010
end
1111

1212
module PuppetLanguageServer
1313
module PuppetHelper
1414
# Reference - https://github.com/puppetlabs/puppet/blob/master/lib/puppet/reference/type.rb
1515

16-
@ops_lock_types = Mutex.new
17-
@ops_lock_funcs = Mutex.new
18-
@ops_lock_classes = Mutex.new
19-
@class_load_info = {}
20-
@function_load_info = {}
21-
@type_load_info = {}
22-
2316
@default_types_loaded = nil
2417
@default_functions_loaded = nil
2518
@default_classes_loaded = nil
19+
@inmemory_cache = nil
20+
21+
def self.configure_cache(options = {})
22+
@inmemory_cache = PuppetLanguageServer::PuppetHelper::Cache.new(options)
23+
end
2624

2725
# Resource Face
2826
def self.resource_face_get_by_typename(typename)
@@ -47,25 +45,20 @@ def self.types_loaded?
4745
end
4846

4947
def self.load_types
48+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
5049
_load_default_types
5150
end
5251

5352
def self.get_type(name)
54-
result = nil
55-
return result if @default_types_loaded.nil?
56-
@ops_lock_types.synchronize do
57-
result = @type_load_info[name.intern]
58-
end
59-
result
53+
return nil if @default_types_loaded == false
54+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
55+
@inmemory_cache.object_by_name(:type, name)
6056
end
6157

6258
def self.type_names
63-
result = []
64-
return result if @default_types_loaded.nil?
65-
@ops_lock_types.synchronize do
66-
result = @type_load_info.keys.map(&:to_s)
67-
end
68-
result
59+
return [] if @default_types_loaded == false
60+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
61+
@inmemory_cache.object_names_by_section(:type).map(&:to_s)
6962
end
7063

7164
# Functions
@@ -74,6 +67,7 @@ def self.functions_loaded?
7467
end
7568

7669
def self.load_functions
70+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
7771
_load_default_functions if @default_functions_loaded.nil?
7872
end
7973

@@ -84,33 +78,29 @@ def self.load_functions_async
8478
end
8579

8680
def self.filtered_function_names(&block)
81+
return [] if @default_functions_loaded == false
82+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
83+
_load_default_functions if @default_functions_loaded.nil?
8784
result = []
88-
return result if @default_functions_loaded.nil?
89-
@ops_lock_funcs.synchronize do
90-
@function_load_info.each do |name, data|
91-
filter = block.call(name, data)
92-
result << name if filter == true
93-
end
85+
@inmemory_cache.objects_by_section(:function) do |name, data|
86+
filter = block.call(name, data)
87+
result << name if filter == true
9488
end
9589
result
9690
end
9791

9892
def self.function(name)
99-
result = nil
100-
return result if @default_functions_loaded.nil?
101-
@ops_lock_funcs.synchronize do
102-
result = @function_load_info[name.intern].dup if @function_load_info.key?(name.intern)
103-
end
104-
result
93+
return nil if @default_functions_loaded == false
94+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
95+
_load_default_functions unless @default_functions_loaded
96+
@inmemory_cache.object_by_name(:function, name)
10597
end
10698

10799
def self.function_names
108-
result = []
109-
return result if @default_functions_loaded.nil?
110-
@ops_lock_funcs.synchronize do
111-
result = @function_load_info.keys.map(&:to_s)
112-
end
113-
result
100+
return [] if @default_functions_loaded == false
101+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
102+
_load_default_functions if @default_functions_loaded.nil?
103+
@inmemory_cache.object_names_by_section(:function).map(&:to_s)
114104
end
115105

116106
# Classes and Defined Types
@@ -119,24 +109,22 @@ def self.classes_loaded?
119109
end
120110

121111
def self.load_classes
112+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
122113
_load_default_classes if @default_classes_loaded.nil?
123114
end
124115

116+
def self.get_class(name)
117+
return nil if @default_classes_loaded == false
118+
raise('Puppet Helper Cache has not been configured') if @inmemory_cache.nil?
119+
@inmemory_cache.object_by_name(:class, name)
120+
end
121+
125122
def self.load_classes_async
126123
Thread.new do
127124
load_classes
128125
end
129126
end
130127

131-
def self.get_class(name)
132-
result = nil
133-
return result if @default_classes_loaded.nil?
134-
@ops_lock_funcs.synchronize do
135-
result = @class_load_info[name.intern].dup if @class_load_info.key?(name.intern)
136-
end
137-
result
138-
end
139-
140128
def self.prune_resource_parameters(resources)
141129
# From https://github.com/puppetlabs/puppet/blob/488661d84e54904124514ab9e4500e81b10f84d1/lib/puppet/application/resource.rb#L146-L148
142130
if resources.is_a?(Array)
@@ -199,6 +187,11 @@ def self._load_default_classes
199187
private_class_method :_load_default_classes
200188

201189
def self.load_classes_from_manifest(manifest_file)
190+
# TODO: Add ignore cache switch
191+
return 0 if @inmemory_cache.exist?(manifest_file, :class)
192+
return 0 if @inmemory_cache.load_from_persistent_cache!(manifest_file)
193+
@inmemory_cache.set(manifest_file, :class, nil)
194+
202195
file_content = File.open(manifest_file, 'r:UTF-8') { |f| f.read }
203196

204197
parser = Puppet::Pops::Parser::Parser.new
@@ -210,7 +203,7 @@ def self.load_classes_from_manifest(manifest_file)
210203
return 0
211204
end
212205

213-
class_info_count = 0
206+
class_info = {}
214207
# Enumerate the entire AST looking for classes and defined types
215208
# TODO: Need to learn how to read the help/docs for hover support
216209
if result.model.respond_to? :eAllContents
@@ -233,10 +226,7 @@ def self.load_classes_from_manifest(manifest_file)
233226

234227
obj = FauxPuppetClass.new
235228
obj.from_puppet!(item.name, puppet_class)
236-
@ops_lock_classes.synchronize do
237-
@class_load_info[item.name.intern] = obj
238-
end
239-
class_info_count += 1
229+
class_info[item.name.intern] = obj
240230
end
241231
else
242232
result.model._pcore_all_contents([]) do |item|
@@ -257,14 +247,12 @@ def self.load_classes_from_manifest(manifest_file)
257247

258248
obj = FauxPuppetClass.new
259249
obj.from_puppet!(item.name, puppet_class)
260-
@ops_lock_classes.synchronize do
261-
@class_load_info[item.name.intern] = obj
262-
end
263-
class_info_count += 1
250+
class_info[item.name.intern] = obj
264251
end
265252
end
266253

267-
class_info_count
254+
@inmemory_cache.set(manifest_file, :class, class_info)
255+
class_info.count
268256
end
269257

270258
def self.load_type_file(name, autoloader, env)
@@ -274,6 +262,9 @@ def self.load_type_file(name, autoloader, env)
274262
PuppetLanguageServer.log_message(:warn, "[PuppetHelper::load_type_file] Could not find absolute path of type #{name}")
275263
return 0
276264
end
265+
# TODO: Add ignore cache switch
266+
return 0 if @inmemory_cache.exist?(absolute_name, :type)
267+
return 0 if @inmemory_cache.load_from_persistent_cache!(absolute_name)
277268

278269
# Get the list of currently loaded types
279270
loaded_types = []
@@ -290,6 +281,7 @@ def self.load_type_file(name, autoloader, env)
290281
end
291282
end
292283

284+
@inmemory_cache.set(absolute_name, :type, nil)
293285
unless autoloader.loaded?(name)
294286
# This is an expensive call
295287
unless autoloader.load(name)
@@ -298,7 +290,7 @@ def self.load_type_file(name, autoloader, env)
298290
end
299291

300292
# Find the types that were loaded
301-
type_count = 0
293+
types = {}
302294
# Due to PUP-8301, if no types have been loaded yet then Puppet::Type.eachtype
303295
# will throw instead of not yielding.
304296
begin
@@ -308,10 +300,10 @@ def self.load_type_file(name, autoloader, env)
308300
next if item.name == :component || item.name == :whit
309301
obj = FauxType.new
310302
obj.from_puppet!(item.name, item)
311-
@ops_lock_types.synchronize do
312-
@type_load_info[obj.key] = obj
313-
end
314-
type_count += 1
303+
# TODO: Need to use calling_source in the cache backing store
304+
# Perhaps I should be incrementally adding items to the cache instead of batch mode?
305+
obj.calling_source = absolute_name
306+
types[obj.key] = obj
315307
end
316308
rescue NoMethodError => detail
317309
# Detect PUP-8301
@@ -322,9 +314,10 @@ def self.load_type_file(name, autoloader, env)
322314
end
323315
end
324316

325-
PuppetLanguageServer.log_message(:warn, "[PuppetHelper::load_type_file] type #{absolute_name} did not load any types") if type_count.zero?
317+
PuppetLanguageServer.log_message(:warn, "[PuppetHelper::load_type_file] type #{absolute_name} did not load any types") if types.empty?
318+
@inmemory_cache.set(absolute_name, :type, types)
326319

327-
type_count
320+
types.count
328321
end
329322
private_class_method :load_type_file
330323

@@ -361,9 +354,13 @@ def self.load_function_file(name, autoloader, env)
361354
PuppetLanguageServer.log_message(:warn, "[PuppetHelper::load_function_file] Could not find absolute path of function #{name}")
362355
return 0
363356
end
357+
# TODO: Add ignore cache switch
358+
return 0 if @inmemory_cache.exist?(absolute_name, :function)
359+
return 0 if @inmemory_cache.load_from_persistent_cache!(absolute_name)
364360

365361
function_module = Puppet::Parser::Functions.environment_module(env)
366362
function_count = 0
363+
@inmemory_cache.set(absolute_name, :function, nil)
367364
unless autoloader.loaded?(name)
368365
# This is an expensive call
369366
unless autoloader.load(name, env)
@@ -379,14 +376,11 @@ def self.load_function_file(name, autoloader, env)
379376
obj = FauxFunction.new
380377
obj.from_puppet!(func_name, item)
381378
obj.calling_source = absolute_name
382-
@ops_lock_funcs.synchronize do
383-
@function_load_info[obj.key] = obj
384-
end
385-
386379
funcs[obj.key] = obj
387380
function_count += 1
388381
end
389382
PuppetLanguageServer.log_message(:warn, "[PuppetHelper::load_function_file] file #{absolute_name} did load any functions") if function_count.zero?
383+
@inmemory_cache.set(absolute_name, :function, funcs)
390384

391385
function_count
392386
end
@@ -412,16 +406,17 @@ def self._load_default_functions
412406
filenames.uniq!.compact!
413407
# Now add the functions in each file to the cache
414408
filenames.each do |filename|
409+
@inmemory_cache.set(filename, :function, nil)
410+
funcs = {}
415411
function_module.all_function_info
416412
.select { |_k, i| filename.casecmp(i[:source_location][:source].to_s).zero? }
417413
.each do |name, item|
418414
obj = FauxFunction.new
419415
obj.from_puppet!(name, item)
420-
@ops_lock_funcs.synchronize do
421-
@function_load_info[obj.key] = obj
422-
end
416+
funcs[obj.key] = obj
423417
function_count += 1
424418
end
419+
@inmemory_cache.set(filename, :function, funcs)
425420
end
426421

427422
# Now we can load functions from the default locations

0 commit comments

Comments
 (0)