class Transaction::Transaction::Relationship_graph_wrapper

We want to monitor changes in the relationship graph of our catalog but this is complicated by the fact that the catalog both is_a graph and has_a graph, by the fact that changes to the structure of the object can have adverse serialization effects, by threading issues, by order-of-initialization issues, etc.

Since the proper lifetime/scope of the monitoring is a transaction and the transaction is already commiting a mild law-of-demeter transgression, we cut the Gordian knot here by simply wrapping the transaction’s view of the resource graph to capture and maintain the information we need. Nothing outside the transaction needs this information, and nothing outside the transaction can see it except via the Transaction#relationship_graph

Attributes

blockers[R]
done[R]
generated[R]
ready[R]
real_graph[R]
transaction[R]
unguessable_deterministic_key[R]

Public Class Methods

new(real_graph,transaction) click to toggle source
# File lib/puppet/transaction.rb, line 308
def initialize(real_graph,transaction)
  @real_graph = real_graph
  @transaction = transaction
  @ready = Puppet::RbTreeMap.new
  @generated = {}
  @done = {}
  @blockers = {}
  @unguessable_deterministic_key = Hash.new { |h,k| h[k] = Digest::SHA1.hexdigest("NaCl, MgSO4 (salts) and then #{k.ref}") }
  @providerless_types = []
end

Public Instance Methods

add_edge(f,t,label=nil) click to toggle source
# File lib/puppet/transaction.rb, line 324
def add_edge(f,t,label=nil)
  key = unguessable_deterministic_key[t]

  ready.delete(key)

  real_graph.add_edge(f,t,label)
end
add_vertex(v) click to toggle source
# File lib/puppet/transaction.rb, line 321
def add_vertex(v)
  real_graph.add_vertex(v)
end
enqueue(*resources) click to toggle source
# File lib/puppet/transaction.rb, line 349
def enqueue(*resources)
  resources.each do |resource|
    key = unguessable_deterministic_key[resource]
    ready[key] = resource
  end
end
enqueue_roots() click to toggle source

Enqueue the initial set of resources, those with no dependencies.

# File lib/puppet/transaction.rb, line 332
def enqueue_roots
  vertices.each do |v|
    blockers[v] = direct_dependencies_of(v).length
    enqueue(v) if blockers[v] == 0
  end
end
fail_unsuitable_resources(resources) click to toggle source
# File lib/puppet/transaction.rb, line 407
def fail_unsuitable_resources(resources)
  resources.each do |resource|
    # We don't automatically assign unsuitable providers, so if there
    # is one, it must have been selected by the user.
    if resource.provider
      resource.err "Provider #{resource.provider.class.name} is not functional on this host"
    else
      @providerless_types << resource.type
    end

    transaction.resource_status(resource).failed = true

    finish(resource)
  end
end
finish(resource) click to toggle source
# File lib/puppet/transaction.rb, line 355
def finish(resource)
  direct_dependents_of(resource).each do |v|
    enqueue(v) if unblock(v)
  end
  done[resource] = true
end
method_missing(*args,&block) click to toggle source
# File lib/puppet/transaction.rb, line 318
def method_missing(*args,&block)
  real_graph.send(*args,&block)
end
next_resource() click to toggle source
# File lib/puppet/transaction.rb, line 361
def next_resource
  ready.delete_min
end
traverse() { |resource| ... } click to toggle source
# File lib/puppet/transaction.rb, line 364
def traverse(&block)
  real_graph.report_cycles_in_graph

  enqueue_roots

  deferred_resources = []

  while (resource = next_resource) && !transaction.stop_processing?
    if resource.suitable?
      made_progress = true

      transaction.prefetch_if_necessary(resource)

      # If we generated resources, we don't know what they are now
      # blocking, so we opt to recompute it, rather than try to track every
      # change that would affect the number.
      blockers.clear if transaction.eval_generate(resource)

      yield resource

      finish(resource)
    else
      deferred_resources << resource
    end

    if ready.empty? and deferred_resources.any?
      if made_progress
        enqueue(*deferred_resources)
      else
        fail_unsuitable_resources(deferred_resources)
      end

      made_progress = false
      deferred_resources = []
    end
  end

  # Just once per type. No need to punish the user.
  @providerless_types.uniq.each do |type|
    Puppet.err "Could not find a suitable provider for #{type}"
  end
end
unblock(resource) click to toggle source

Decrement the blocker count for the resource by 1. If the number of blockers is unknown, count them and THEN decrement by 1.

# File lib/puppet/transaction.rb, line 340
def unblock(resource)
  blockers[resource] ||= direct_dependencies_of(resource).select { |r2| !done[r2] }.length
  if blockers[resource] > 0
    blockers[resource] -= 1
  else
    resource.warning "appears to have a negative number of dependencies"
  end
  blockers[resource] <= 0
end