scout-gear 10.11.4 → 10.11.7

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.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +17 -2
  3. data/VERSION +1 -1
  4. data/bin/scout +10 -10
  5. data/lib/scout/association/fields.rb +15 -15
  6. data/lib/scout/association/index.rb +6 -6
  7. data/lib/scout/association/item.rb +18 -8
  8. data/lib/scout/association.rb +4 -4
  9. data/lib/scout/entity/identifiers.rb +5 -5
  10. data/lib/scout/entity/property.rb +2 -2
  11. data/lib/scout/entity.rb +1 -1
  12. data/lib/scout/knowledge_base/description.rb +10 -10
  13. data/lib/scout/knowledge_base/entity.rb +6 -6
  14. data/lib/scout/knowledge_base/list.rb +1 -1
  15. data/lib/scout/knowledge_base/query.rb +4 -4
  16. data/lib/scout/knowledge_base/registry.rb +6 -6
  17. data/lib/scout/knowledge_base/traverse.rb +7 -40
  18. data/lib/scout/persist/engine/fix_width_table.rb +6 -6
  19. data/lib/scout/persist/engine/packed_index.rb +2 -2
  20. data/lib/scout/persist/engine/sharder.rb +4 -4
  21. data/lib/scout/persist/engine/tkrzw.rb +1 -1
  22. data/lib/scout/persist/engine/tokyocabinet.rb +2 -2
  23. data/lib/scout/persist/tsv/adapter/fix_width_table.rb +1 -1
  24. data/lib/scout/persist/tsv/adapter/packed_index.rb +1 -1
  25. data/lib/scout/persist/tsv/adapter/tkrzw.rb +1 -1
  26. data/lib/scout/persist/tsv/adapter/tokyocabinet.rb +3 -3
  27. data/lib/scout/persist/tsv/serialize.rb +3 -3
  28. data/lib/scout/persist/tsv.rb +1 -1
  29. data/lib/scout/semaphore.rb +100 -17
  30. data/lib/scout/tsv/annotation/repo.rb +4 -4
  31. data/lib/scout/tsv/annotation.rb +2 -2
  32. data/lib/scout/tsv/attach.rb +7 -7
  33. data/lib/scout/tsv/change_id/translate.rb +1 -1
  34. data/lib/scout/tsv/csv.rb +3 -3
  35. data/lib/scout/tsv/dumper.rb +8 -8
  36. data/lib/scout/tsv/index.rb +1 -1
  37. data/lib/scout/tsv/open.rb +3 -3
  38. data/lib/scout/tsv/stream.rb +2 -2
  39. data/lib/scout/tsv/traverse.rb +4 -4
  40. data/lib/scout/tsv/util/filter.rb +9 -9
  41. data/lib/scout/tsv/util/process.rb +2 -2
  42. data/lib/scout/tsv/util/reorder.rb +2 -2
  43. data/lib/scout/tsv/util/select.rb +3 -3
  44. data/lib/scout/tsv/util/unzip.rb +2 -2
  45. data/lib/scout/tsv/util.rb +1 -1
  46. data/lib/scout/tsv.rb +2 -2
  47. data/lib/scout/work_queue/socket.rb +3 -2
  48. data/lib/scout/work_queue/worker.rb +4 -4
  49. data/lib/scout/work_queue.rb +7 -7
  50. data/lib/scout/workflow/definition.rb +18 -16
  51. data/lib/scout/workflow/deployment/local.rb +81 -62
  52. data/lib/scout/workflow/deployment/orchestrator/batches.rb +66 -5
  53. data/lib/scout/workflow/deployment/orchestrator/chains.rb +47 -30
  54. data/lib/scout/workflow/deployment/orchestrator/rules.rb +3 -3
  55. data/lib/scout/workflow/deployment/orchestrator/workload.rb +11 -22
  56. data/lib/scout/workflow/deployment/scheduler/job.rb +34 -36
  57. data/lib/scout/workflow/deployment/scheduler/lfs.rb +1 -1
  58. data/lib/scout/workflow/deployment/scheduler/pbs.rb +4 -4
  59. data/lib/scout/workflow/deployment/scheduler/slurm.rb +2 -2
  60. data/lib/scout/workflow/deployment/scheduler.rb +23 -12
  61. data/lib/scout/workflow/deployment/trace.rb +2 -2
  62. data/lib/scout/workflow/documentation.rb +4 -4
  63. data/lib/scout/workflow/export.rb +1 -1
  64. data/lib/scout/workflow/path.rb +2 -2
  65. data/lib/scout/workflow/step/children.rb +1 -1
  66. data/lib/scout/workflow/step/dependencies.rb +36 -3
  67. data/lib/scout/workflow/step/info.rb +5 -19
  68. data/lib/scout/workflow/step/inputs.rb +1 -1
  69. data/lib/scout/workflow/step/progress.rb +2 -2
  70. data/lib/scout/workflow/step/provenance.rb +4 -4
  71. data/lib/scout/workflow/step/status.rb +23 -9
  72. data/lib/scout/workflow/step.rb +21 -19
  73. data/lib/scout/workflow/task/dependencies.rb +10 -3
  74. data/lib/scout/workflow/task/info.rb +3 -3
  75. data/lib/scout/workflow/task/inputs.rb +8 -8
  76. data/lib/scout/workflow/task.rb +37 -22
  77. data/lib/scout/workflow/usage.rb +13 -13
  78. data/lib/scout/workflow/util.rb +1 -1
  79. data/lib/scout/workflow.rb +6 -6
  80. data/scout-gear.gemspec +4 -3
  81. data/scout_commands/alias +1 -1
  82. data/scout_commands/batch/clean +12 -12
  83. data/scout_commands/batch/list +26 -25
  84. data/scout_commands/batch/tail +9 -5
  85. data/scout_commands/cat +1 -1
  86. data/scout_commands/doc +2 -2
  87. data/scout_commands/entity +4 -4
  88. data/scout_commands/find +1 -1
  89. data/scout_commands/kb/config +1 -1
  90. data/scout_commands/kb/entities +1 -1
  91. data/scout_commands/kb/list +1 -1
  92. data/scout_commands/kb/query +2 -2
  93. data/scout_commands/kb/register +1 -1
  94. data/scout_commands/kb/show +1 -1
  95. data/scout_commands/kb/traverse +1 -1
  96. data/scout_commands/log +6 -6
  97. data/scout_commands/resource/produce +2 -2
  98. data/scout_commands/resource/sync +1 -1
  99. data/scout_commands/system/clean +7 -7
  100. data/scout_commands/system/status +4 -4
  101. data/scout_commands/template +1 -1
  102. data/scout_commands/update +1 -1
  103. data/scout_commands/workflow/cmd +2 -1
  104. data/scout_commands/workflow/example +123 -0
  105. data/scout_commands/workflow/info +10 -1
  106. data/scout_commands/workflow/install +1 -1
  107. data/scout_commands/workflow/list +2 -2
  108. data/scout_commands/workflow/process +2 -2
  109. data/scout_commands/workflow/prov +3 -3
  110. data/scout_commands/workflow/task +36 -11
  111. data/scout_commands/workflow/trace +1 -1
  112. data/scout_commands/workflow/write_info +2 -2
  113. data/share/templates/command +1 -1
  114. data/test/scout/association/test_item.rb +5 -0
  115. data/test/scout/entity/test_property.rb +3 -3
  116. data/test/scout/knowledge_base/test_description.rb +1 -1
  117. data/test/scout/knowledge_base/test_traverse.rb +2 -2
  118. data/test/scout/persist/engine/test_packed_index.rb +6 -6
  119. data/test/scout/persist/test_tsv.rb +4 -4
  120. data/test/scout/persist/tsv/adapter/test_packed_index.rb +4 -4
  121. data/test/scout/persist/tsv/adapter/test_sharder.rb +23 -23
  122. data/test/scout/persist/tsv/adapter/test_tokyocabinet.rb +1 -1
  123. data/test/scout/persist/tsv/test_serialize.rb +1 -1
  124. data/test/scout/test_association.rb +1 -1
  125. data/test/scout/test_tsv.rb +2 -2
  126. data/test/scout/test_workflow.rb +2 -2
  127. data/test/scout/tsv/test_annotation.rb +4 -4
  128. data/test/scout/tsv/test_index.rb +1 -1
  129. data/test/scout/tsv/test_open.rb +2 -2
  130. data/test/scout/tsv/test_parser.rb +2 -2
  131. data/test/scout/tsv/test_stream.rb +1 -1
  132. data/test/scout/tsv/test_transformer.rb +1 -1
  133. data/test/scout/tsv/util/test_filter.rb +1 -1
  134. data/test/scout/tsv/util/test_melt.rb +1 -1
  135. data/test/scout/tsv/util/test_reorder.rb +1 -1
  136. data/test/scout/work_queue/test_socket.rb +3 -3
  137. data/test/scout/work_queue/test_worker.rb +2 -2
  138. data/test/scout/workflow/deployment/orchestrator/test_batches.rb +13 -3
  139. data/test/scout/workflow/deployment/orchestrator/test_chains.rb +15 -13
  140. data/test/scout/workflow/deployment/orchestrator/test_workload.rb +1 -1
  141. data/test/scout/workflow/deployment/test_local.rb +2 -2
  142. data/test/scout/workflow/deployment/test_scheduler.rb +1 -2
  143. data/test/scout/workflow/step/test_children.rb +1 -1
  144. data/test/scout/workflow/step/test_dependencies.rb +36 -1
  145. data/test/scout/workflow/step/test_info.rb +3 -35
  146. data/test/scout/workflow/step/test_load.rb +1 -1
  147. data/test/scout/workflow/step/test_provenance.rb +1 -1
  148. data/test/scout/workflow/step/test_status.rb +33 -1
  149. data/test/scout/workflow/task/test_dependencies.rb +9 -7
  150. data/test/scout/workflow/task/test_inputs.rb +1 -1
  151. data/test/scout/workflow/test_definition.rb +1 -1
  152. data/test/scout/workflow/test_documentation.rb +1 -1
  153. data/test/scout/workflow/test_entity.rb +2 -2
  154. data/test/scout/workflow/test_step.rb +13 -13
  155. data/test/scout/workflow/test_usage.rb +1 -1
  156. data/test/test_helper.rb +1 -1
  157. metadata +3 -2
@@ -45,7 +45,7 @@ class KnowledgeBase
45
45
 
46
46
  [source_entities, target_entities]
47
47
  end
48
-
48
+
49
49
  def reassign(matches, source, target)
50
50
  #assignments[source] = (matches.any? ? matches.collect{|m| m.source_entity }.uniq : nil) if is_wildcard? source
51
51
  #assignments[target] = (matches.any? ? matches.collect{|m| m.target_entity }.uniq : nil) if is_wildcard? target
@@ -64,7 +64,7 @@ class KnowledgeBase
64
64
  assigned = assignments[source] || []
65
65
  matches = matches.select{|m| assigned.include? m.partition("~").first }
66
66
  end
67
-
67
+
68
68
  if is_wildcard? target
69
69
  assigned = assignments[target] || []
70
70
  matches = matches.select{|m| assigned.include? m.partition("~").last }
@@ -110,7 +110,7 @@ class KnowledgeBase
110
110
 
111
111
  return false if paths.empty?
112
112
 
113
- paths
113
+ paths
114
114
  end
115
115
 
116
116
  def _ep(paths)
@@ -172,8 +172,8 @@ class KnowledgeBase
172
172
  _name, _sep, _kb = db.partition("@")
173
173
  case
174
174
  when _name[0] == '?'
175
- dbs = all_dbs.select{|_db|
176
- n,_s,d=_db.partition("@");
175
+ dbs = all_dbs.select{|_db|
176
+ n,_s,d=_db.partition("@");
177
177
  d.nil? or d.empty? or (d == _kb and assignments[_name].include?(n))
178
178
  }
179
179
  when _kb[0] == '?'
@@ -247,7 +247,7 @@ class KnowledgeBase
247
247
  else
248
248
  dbs = id_dbs(db)
249
249
  names = names.collect{|name| assignments.include?(name) ? assignments[name] : name}.flatten
250
- ids = names.collect{|name|
250
+ ids = names.collect{|name|
251
251
  id = nil
252
252
  dbs.each do |db|
253
253
  sid, tid = identify db, name, name
@@ -286,44 +286,11 @@ class KnowledgeBase
286
286
  [assignments, paths]
287
287
  end
288
288
 
289
- #def traverse
290
- # all_matches = []
291
-
292
- # rules.each do |rule|
293
- # rule = rule.strip
294
- # next if rule.empty?
295
- # source, db, target, conditions = rule.match(/([^\s]+)\s+([^\s]+)\s+([^\s]+)(?:\s+-\s+([^\s]+))?/).captures
296
-
297
- # source_entities, target_entities = identify db, source, target
298
-
299
- # matches = kb.subset(db, :source => source_entities, :target => target_entities)
300
-
301
- # if conditions
302
- # conditions.split(/\s+/).each do |condition|
303
- # if condition.index "="
304
- # key, value = conditions.split("=")
305
- # matches = matches.select{|m| m.info[key.strip].to_s =~ /\b#{value.strip}\b/}
306
- # else
307
- # matches = matches.select{|m| m.info[condition.strip].to_s =~ /\btrue\b/}
308
- # end
309
- # end
310
- # end
311
-
312
- # reassign matches, source, target
313
-
314
- # all_matches << matches
315
- # end
316
-
317
- # paths = find_paths rules, all_matches, assignments
318
-
319
- # [assignments, paths]
320
- #end
321
-
322
289
  end
323
290
 
324
291
  def traverse(rules, nopaths=false)
325
292
  traverser = KnowledgeBase::Traverser.new self, rules
326
293
  traverser.traverse nopaths
327
294
  end
328
-
295
+
329
296
  end
@@ -62,7 +62,7 @@ class FixWidthTable
62
62
  Persist::CONNECTIONS[persistence_path] = self.new(persistence_path, value_size, range, update)
63
63
  end
64
64
 
65
- Persist::CONNECTIONS[persistence_path]
65
+ Persist::CONNECTIONS[persistence_path]
66
66
  end
67
67
 
68
68
  def format(pos, value)
@@ -209,10 +209,10 @@ class FixWidthTable
209
209
  values = []
210
210
  l_start = idx_pos(idx)
211
211
  l_end = idx_pos_end(idx)
212
-
212
+
213
213
  if return_idx
214
214
  while l_start <= r_end
215
- values << idx if l_end >= r_start
215
+ values << idx if l_end >= r_start
216
216
  idx += 1
217
217
  break if idx >= size
218
218
  l_start = idx_pos(idx)
@@ -220,7 +220,7 @@ class FixWidthTable
220
220
  end
221
221
  else
222
222
  while l_start <= r_end
223
- values << idx_value(idx) if l_end >= r_start
223
+ values << idx_value(idx) if l_end >= r_start
224
224
  idx += 1
225
225
  break if idx >= size
226
226
  l_start = idx_pos(idx)
@@ -254,7 +254,7 @@ class FixWidthTable
254
254
  values = []
255
255
  l_start = idx_pos(idx)
256
256
  l_end = idx_pos_end(idx)
257
- if return_idx
257
+ if return_idx
258
258
  while l_start <= r_end
259
259
  values << idx
260
260
  idx += 1
@@ -284,7 +284,7 @@ class FixWidthTable
284
284
  get_point(pos)
285
285
  end
286
286
  end
287
-
287
+
288
288
  def overlaps(pos, value = false)
289
289
  return [] if size == 0
290
290
  idxs = if @range
@@ -56,7 +56,7 @@ class PackedIndex
56
56
  def file
57
57
  @persistence_path
58
58
  end
59
-
59
+
60
60
  def close
61
61
  @stream.close
62
62
  end
@@ -82,7 +82,7 @@ class PackedIndex
82
82
  def get_position(position)
83
83
  @stream.seek(position * item_size + offset)
84
84
  encoded = @stream.read(item_size)
85
- return nil if encoded.nil? or encoded == nil_string
85
+ return nil if encoded.nil? or encoded == nil_string
86
86
  encoded.unpack mask
87
87
  end
88
88
 
@@ -10,7 +10,7 @@ class Sharder
10
10
  @db_type = db_type
11
11
 
12
12
  if write
13
- @databases = {}
13
+ @databases = {}
14
14
  end
15
15
  end
16
16
 
@@ -210,9 +210,9 @@ class Sharder
210
210
  end
211
211
 
212
212
  def size
213
- databases.inject(0){|acc,i|
214
- shard, db = i;
215
- acc += db.size
213
+ databases.inject(0){|acc,i|
214
+ shard, db = i;
215
+ acc += db.size
216
216
  }
217
217
  end
218
218
  end
@@ -5,7 +5,7 @@ module ScoutTKRZW
5
5
 
6
6
  def self.open(path, write = true, persistence_class = 'tkh', options = {})
7
7
  open_options = IndiferentHash.add_defaults options, truncate: true, num_buckets: 100, dbm: "HashDBM", sync_hard: true, encoding: "UTF-8"
8
-
8
+
9
9
  path = path.find if Path === path
10
10
 
11
11
  dir = File.dirname(File.expand_path(path))
@@ -31,11 +31,11 @@ if continue
31
31
  database = Log.ignore_stderr do Persist::CONNECTIONS[path] ||= tokyocabinet_class.new end
32
32
 
33
33
  if big and not Open.exists?(path)
34
- database.tune(nil, nil, nil, tokyocabinet_class::TLARGE | tokyocabinet_class::TDEFLATE)
34
+ database.tune(nil, nil, nil, tokyocabinet_class::TLARGE | tokyocabinet_class::TDEFLATE)
35
35
  end
36
36
 
37
37
  flags = (write ? tokyocabinet_class::OWRITER | tokyocabinet_class::OCREAT : tokyocabinet_class::OREADER)
38
- database.close
38
+ database.close
39
39
 
40
40
  if !database.open(path, flags)
41
41
  ecode = database.ecode
@@ -101,6 +101,6 @@ Persist.save_drivers[:fwt] = proc do |file, content|
101
101
  Misc.sensiblewrite(file, content.file.read)
102
102
  end
103
103
 
104
- Persist.load_drivers[:fwt] = proc do |file|
104
+ Persist.load_drivers[:fwt] = proc do |file|
105
105
  FixWidthTable.new file
106
106
  end
@@ -44,7 +44,7 @@ module PKIAdapter
44
44
  end
45
45
 
46
46
  def add(key, value)
47
- key = pos_function.call(key) if pos_function
47
+ key = pos_function.call(key) if pos_function
48
48
  if Numeric === key
49
49
  @_last ||= -1
50
50
  skipped = key - @_last - 1
@@ -11,7 +11,7 @@ Persist.save_drivers[:tkh] = proc do |file, content|
11
11
  data
12
12
  end
13
13
 
14
- Persist.load_drivers[:tkh] = proc do |file|
14
+ Persist.load_drivers[:tkh] = proc do |file|
15
15
  data = ScoutTKRZW.open(file, false, "tkh")
16
16
  data.extend TSVAdapter unless TSVAdapter === data
17
17
  data
@@ -1,7 +1,7 @@
1
1
  require_relative 'base'
2
2
  require_relative '../../engine/tokyocabinet'
3
3
 
4
- module TKAdapter
4
+ module TKAdapter
5
5
  include TSVAdapter
6
6
  def self.extended(obj)
7
7
  obj.extend TSVAdapter
@@ -38,7 +38,7 @@ Persist.save_drivers[:HDB] = proc do |file, content|
38
38
  end
39
39
  end
40
40
 
41
- Persist.load_drivers[:HDB] = proc do |file|
41
+ Persist.load_drivers[:HDB] = proc do |file|
42
42
  data = ScoutCabinet.open(file, false, "HDB")
43
43
  data.extend TKAdapter unless TKAdapter === data
44
44
  data
@@ -58,7 +58,7 @@ Persist.save_drivers[:BDB] = proc do |file, content|
58
58
  end
59
59
  end
60
60
 
61
- Persist.load_drivers[:BDB] = proc do |file|
61
+ Persist.load_drivers[:BDB] = proc do |file|
62
62
  data = ScoutCabinet.open(file, false, "BDB")
63
63
  data.extend TKAdapter unless TKAdapter === data
64
64
  data
@@ -10,7 +10,7 @@ module TSVAdapter
10
10
  def self.dump(o); [o].pack('m'); end
11
11
  def self.load(str); str.unpack('m').first; end
12
12
  end
13
-
13
+
14
14
  class IntegerSerializer
15
15
  def self.dump(i); [i].pack("l"); end
16
16
  def self.load(str); str.unpack("l").first; end
@@ -101,8 +101,8 @@ module TSVAdapter
101
101
  :flat => StringArraySerializer,
102
102
  :double => StringDoubleArraySerializer,
103
103
  :clean => CleanSerializer,
104
- :integer => IntegerSerializer,
105
- :float => FloatSerializer,
104
+ :integer => IntegerSerializer,
105
+ :float => FloatSerializer,
106
106
  :integer_array => IntegerArraySerializer,
107
107
  :float_array => FloatArraySerializer,
108
108
  :strict_integer_array => StrictIntegerArraySerializer,
@@ -2,7 +2,7 @@ require 'scout/persist'
2
2
  require_relative 'engine'
3
3
  require_relative 'tsv/adapter'
4
4
 
5
- Persist.save_drivers[:tsv] = proc do |file,content|
5
+ Persist.save_drivers[:tsv] = proc do |file,content|
6
6
  case content
7
7
  when IO
8
8
  Open.sensible_write(file, content)
@@ -1,5 +1,6 @@
1
1
  begin
2
2
  require 'inline'
3
+ require 'fileutils'
3
4
  continue = true
4
5
  rescue Exception
5
6
  Log.warn "The RubyInline gem could not be loaded: semaphore synchronization will not work"
@@ -25,7 +26,7 @@ if continue
25
26
 
26
27
  # Create a named semaphore. Return 0 on success, -errno on error.
27
28
  builder.c_singleton <<-EOF
28
- int create_semaphore(char* name, int value){
29
+ int create_semaphore_c(char* name, int value){
29
30
  sem_t* sem;
30
31
  sem = sem_open(name, O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO, value);
31
32
  if (sem == SEM_FAILED){
@@ -39,7 +40,7 @@ if continue
39
40
 
40
41
  # Unlink (remove) a named semaphore. Return 0 on success, -errno on error.
41
42
  builder.c_singleton <<-EOF
42
- int delete_semaphore(char* name){
43
+ int delete_semaphore_c(char* name){
43
44
  int ret = sem_unlink(name);
44
45
  if (ret == -1) {
45
46
  return -errno;
@@ -50,7 +51,7 @@ if continue
50
51
 
51
52
  # Wait (sem_wait) on a named semaphore. Return 0 on success, -errno on error.
52
53
  builder.c_singleton <<-EOF
53
- int wait_semaphore(char* name){
54
+ int wait_semaphore_c(char* name){
54
55
  sem_t* sem;
55
56
  sem = sem_open(name, 0);
56
57
  if (sem == SEM_FAILED){
@@ -76,7 +77,7 @@ if continue
76
77
 
77
78
  # Post (sem_post) on a named semaphore. Return 0 on success, -errno on error.
78
79
  builder.c_singleton <<-EOF
79
- int post_semaphore(char* name){
80
+ int post_semaphore_c(char* name){
80
81
  sem_t* sem;
81
82
  sem = sem_open(name, 0);
82
83
  if (sem == SEM_FAILED){
@@ -113,6 +114,11 @@ if continue
113
114
  s
114
115
  end
115
116
 
117
+ def self.exists?(name)
118
+ file = File.join('/dev/shm', 'sem.' + name[1..-1])
119
+ Open.exists? file
120
+ end
121
+
116
122
  # Errno numeric lists
117
123
  RETRIABLE_ERRNOS = [
118
124
  Errno::ENOENT,
@@ -154,25 +160,87 @@ if continue
154
160
  end
155
161
  end
156
162
 
163
+ # Try to create the semaphore while holding a per-semaphore lock to avoid races
164
+ def self.ensure_or_create(name, size = 1)
165
+ # Normalize and make a safe lock path under Scout.tmp.semaphore_locks
166
+ lock_dir = if defined?(Scout) && Scout.respond_to?(:tmp) && Scout.tmp.respond_to?(:semaphore_locks)
167
+ Scout.tmp.semaphore_locks
168
+ else
169
+ File.join('/tmp', 'scout', 'semaphore_locks')
170
+ end
171
+
172
+ FileUtils.mkdir_p(lock_dir) unless File.exist?(lock_dir)
173
+
174
+ lock_base = File.join(lock_dir, name.gsub(%r{^/+}, '').gsub('/', '_'))
175
+
176
+ begin
177
+ Open.lock(lock_base) do |_lf|
178
+ # If someone else created it while waiting for the lock, we're done
179
+ return true if self.exists?(name)
180
+
181
+ Log.info "Semaphore #{name} missing; creating under lock #{lock_base}"
182
+ begin
183
+ # call the lower-level C create and let create_semaphore perform checks/retries
184
+ ret = ScoutSemaphore.create_semaphore_c(name, size)
185
+ if ret < 0
186
+ Log.warn "create_semaphore_c failed for #{name}: errno=#{-ret}"
187
+ return false
188
+ end
189
+
190
+ # best-effort: ensure the file shows up
191
+ unless self.exists?(name)
192
+ Log.warn "Semaphore #{name} created but /dev/shm entry not visible"
193
+ end
194
+
195
+ Log.info "Semaphore #{name} created"
196
+ return true
197
+ rescue Exception => e
198
+ Log.warn "Exception while creating semaphore #{name}: #{e.message}"
199
+ return false
200
+ end
201
+ end
202
+ rescue Exception => e
203
+ Log.warn "Failed to acquire creation lock for #{name}: #{e.message}"
204
+ return false
205
+ end
206
+ end
207
+
157
208
  # Safe wrappers that raise SystemCallError on final failure
158
- def self.safe_create_semaphore(name, value, **opts)
159
- ret = with_retry(**opts) { ScoutSemaphore.create_semaphore(name, value) }
209
+ def self.create_semaphore(name, value, **opts)
210
+ ret = with_retry(**opts) { ScoutSemaphore.create_semaphore_c(name, value) }
211
+ # After creation attempt, make sure the /dev/shm entry exists (cluster may remove entries)
212
+ raise SystemCallError.new("Semaphore missing (#{name})") unless self.exists?(name)
160
213
  if ret < 0
161
214
  raise SystemCallError.new("create_semaphore(#{name}) failed", -ret)
162
215
  end
163
216
  ret
164
217
  end
165
218
 
166
- def self.safe_delete_semaphore(name, **opts)
167
- ret = with_retry(**opts) { ScoutSemaphore.delete_semaphore(name) }
219
+ def self.delete_semaphore(name, **opts)
220
+ ret = with_retry(**opts) { ScoutSemaphore.delete_semaphore_c(name) }
168
221
  if ret < 0
169
- raise SystemCallError.new("delete_semaphore(#{name}) failed", -ret)
222
+ Log.warn("delete_semaphore(#{name}) failed")
170
223
  end
171
224
  ret
172
225
  end
173
226
 
174
- def self.safe_wait_semaphore(name, **opts)
175
- ret = with_retry(**opts) { ScoutSemaphore.wait_semaphore(name) }
227
+ def self.wait_semaphore(name, **opts)
228
+ # Try a normal wait first
229
+ ret = with_retry(**opts) { ScoutSemaphore.wait_semaphore_c(name) }
230
+
231
+ if ret < 0
232
+ err = -ret
233
+ # If semaphore missing or removed, try to recreate it under a lock and retry once
234
+ if err == Errno::ENOENT.new.errno || err == Errno::EIDRM.new.errno
235
+ Log.warn "wait_semaphore: semaphore #{name} appears missing (errno=#{err}); attempting recreate"
236
+ created = ensure_or_create(name, opts.fetch(:create_size, 1))
237
+ if created
238
+ # retry the wait after creating
239
+ ret = with_retry(**opts) { ScoutSemaphore.wait_semaphore_c(name) }
240
+ end
241
+ end
242
+ end
243
+
176
244
  if ret < 0
177
245
  err = -ret
178
246
  if err == Errno::EINTR.new.errno
@@ -181,11 +249,26 @@ if continue
181
249
  raise SystemCallError.new("wait_semaphore(#{name}) failed", err)
182
250
  end
183
251
  end
252
+
184
253
  ret
185
254
  end
186
255
 
187
- def self.safe_post_semaphore(name, **opts)
188
- ret = with_retry(**opts) { ScoutSemaphore.post_semaphore(name) }
256
+ def self.post_semaphore(name, **opts)
257
+ # Try normal post first
258
+ ret = with_retry(**opts) { ScoutSemaphore.post_semaphore_c(name) }
259
+
260
+ if ret < 0
261
+ err = -ret
262
+ # If semaphore missing or removed, try to recreate it under a lock and then post
263
+ if err == Errno::ENOENT.new.errno || err == Errno::EIDRM.new.errno
264
+ Log.warn "post_semaphore: semaphore #{name} appears missing (errno=#{err}); attempting recreate"
265
+ created = ensure_or_create(name, opts.fetch(:create_size, 1))
266
+ if created
267
+ ret = with_retry(**opts) { ScoutSemaphore.post_semaphore_c(name) }
268
+ end
269
+ end
270
+ end
271
+
189
272
  if ret < 0
190
273
  raise SystemCallError.new("post_semaphore(#{name}) failed", -ret)
191
274
  end
@@ -198,7 +281,7 @@ if continue
198
281
 
199
282
  # wait_semaphore returns 0 on success or -errno on error
200
283
  begin
201
- ScoutSemaphore.safe_wait_semaphore(sem)
284
+ ScoutSemaphore.wait_semaphore(sem)
202
285
  rescue SemaphoreInterrupted
203
286
  raise
204
287
  rescue SystemCallError => e
@@ -210,7 +293,7 @@ if continue
210
293
  yield
211
294
  ensure
212
295
  begin
213
- ScoutSemaphore.safe_post_semaphore(sem)
296
+ ScoutSemaphore.post_semaphore(sem)
214
297
  rescue SystemCallError => e
215
298
  # Log but don't raise from ensure
216
299
  # Log.warn "post_semaphore(#{sem}) failed in ensure: #{e.message}"
@@ -232,7 +315,7 @@ if continue
232
315
  begin
233
316
  Log.debug "Creating semaphore (#{ size }): #{file}"
234
317
  begin
235
- ScoutSemaphore.safe_create_semaphore(file, size)
318
+ ScoutSemaphore.create_semaphore(file, size)
236
319
  rescue SystemCallError => e
237
320
  Log.error "Failed to create semaphore #{file}: #{e.message}"
238
321
  raise
@@ -242,7 +325,7 @@ if continue
242
325
  ensure
243
326
  Log.debug "Removing semaphore #{ file }"
244
327
  begin
245
- ScoutSemaphore.safe_delete_semaphore(file)
328
+ ScoutSemaphore.delete_semaphore(file)
246
329
  rescue SystemCallError => e
247
330
  Log.warn "delete_semaphore(#{file}) failed: #{e.message}"
248
331
  end
@@ -56,25 +56,25 @@ module Persist
56
56
  else
57
57
  annotations = yield
58
58
 
59
- repo.write_and_close do
59
+ repo.write_and_close do
60
60
  case
61
61
  when annotations.nil?
62
62
  repo[subkey + "NIL"] = nil
63
63
  when annotations.empty?
64
64
  repo[subkey + "EMPTY"] = nil
65
65
  when (not Array === annotations or (AnnotatedArray === annotations and not Array === annotations.first))
66
- tsv_values = Annotation.obj_tsv_values(annotations, repo_fields)
66
+ tsv_values = Annotation.obj_tsv_values(annotations, repo_fields)
67
67
  repo[subkey + annotations.id << ":" << "SINGLE"] = tsv_values
68
68
  when (not Array === annotations or (AnnotatedArray === annotations and AnnotatedArray === annotations.first))
69
69
  annotations.each_with_index do |e,i|
70
70
  next if e.nil?
71
- tsv_values = Annotation.obj_tsv_values(e, repo_fields)
71
+ tsv_values = Annotation.obj_tsv_values(e, repo_fields)
72
72
  repo[subkey + "ANNOTATED_DOUBLE_ARRAY:" << i.to_s] = tsv_values
73
73
  end
74
74
  else
75
75
  annotations.each_with_index do |e,i|
76
76
  next if e.nil?
77
- tsv_values = Annotation.obj_tsv_values(e, repo_fields)
77
+ tsv_values = Annotation.obj_tsv_values(e, repo_fields)
78
78
  repo[subkey + i.to_s] = tsv_values
79
79
  end
80
80
  end
@@ -41,7 +41,7 @@ module Annotation
41
41
  def self.list_tsv_values(objs, fields)
42
42
  obj_tsv_values(objs, fields)
43
43
  end
44
-
44
+
45
45
 
46
46
  def self.tsv(objs, *fields)
47
47
  return nil if objs.nil?
@@ -76,7 +76,7 @@ module Annotation
76
76
  tsv.key_field = "List"
77
77
 
78
78
  tsv[objs.id] = self.list_tsv_values(objs, fields).dup
79
- when Array === objs
79
+ when Array === objs
80
80
  tsv.key_field = "ID"
81
81
 
82
82
  if Annotation.is_annotated?(objs.compact.first)
@@ -28,7 +28,7 @@ module TSV
28
28
  end
29
29
  end
30
30
 
31
- match_key = source.key_field if match_key.nil?
31
+ match_key = source.key_field if match_key.nil?
32
32
 
33
33
  if other_key.nil?
34
34
  other_key = other.identify_field(match_key)
@@ -87,13 +87,13 @@ module TSV
87
87
  index = TSV.translation_index(identifier_files.flatten, match_key_name, other_key_name)
88
88
  end
89
89
 
90
- if other_key != :key
90
+ if other_key != :key
91
91
  other = other.reorder other_key, fields, one2one: one2one, merge: true, type: :double
92
92
  end
93
93
 
94
- other_field_positions = other.identify_field(fields.dup)
94
+ other_field_positions = other.identify_field(fields.dup)
95
95
  fields.zip(other_field_positions) do |o,n|
96
- raise "Field #{o} not found. Options: #{Log.fingerprint other.fields}" if n.nil?
96
+ raise "Field #{o} not found. Options: #{Log.fingerprint other.fields}" if n.nil?
97
97
  end
98
98
 
99
99
  log_message = "Attach #{Log.fingerprint fields - source.fields} to #{Log.fingerprint source} (#{[match_key, other_key] * "=~"})"
@@ -139,7 +139,7 @@ module TSV
139
139
 
140
140
  if other_values.nil?
141
141
  other_values = empty_other_values
142
- elsif other.type == :flat
142
+ elsif other.type == :flat
143
143
  other_values = [other_values]
144
144
  elsif other.type == :list && source.type == :double
145
145
  other_values = other_values.collect{|v| [v] }
@@ -185,9 +185,9 @@ module TSV
185
185
  end
186
186
  other.each do |other_key,other_values|
187
187
  next if source.include?(other_key)
188
- if other.type == :flat
188
+ if other.type == :flat
189
189
  other_values = [other_values]
190
- elsif other.type == :single
190
+ elsif other.type == :single
191
191
  other_values = [other_values]
192
192
  elsif other.type == :list && type == :double
193
193
  other_values = other_values.collect{|v| [v] }
@@ -87,7 +87,7 @@ module TSV
87
87
  pos = NamedArray.identify_name(TSV.all_fields(file1), TSV.all_fields(file2))
88
88
  TSV.all_fields(file1)[pos.compact.first]
89
89
  end
90
- Persist.persist(name, "HDB", persist_options) do
90
+ Persist.persist(name, "HDB", persist_options) do
91
91
  index = path.inject(nil) do |acc,file|
92
92
  if acc.nil?
93
93
  if source.nil?
data/lib/scout/tsv/csv.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'csv'
2
2
 
3
3
  module TSV
4
- def self.csv(obj, options = {})
4
+ def self.csv(obj, options = {})
5
5
  options = IndiferentHash.add_defaults options, :headers => true, :type => :list
6
6
  headers = options[:headers]
7
7
 
@@ -12,7 +12,7 @@ module TSV
12
12
  merge = options.delete :merge
13
13
  key_field = options.delete :key_field
14
14
  fields = options.delete :fields
15
-
15
+
16
16
  if key_field || fields
17
17
  orig_type = type
18
18
  type = :double
@@ -49,7 +49,7 @@ module TSV
49
49
  else
50
50
  key, *values = row
51
51
  end
52
-
52
+
53
53
  if cast
54
54
  values = values.collect{|v| v.send cast }
55
55
  end