lib/dns-sd/service_instance.rb in dns-sd-0.1.1 vs lib/dns-sd/service_instance.rb in dns-sd-0.1.2

- old
+ new

@@ -1,10 +1,11 @@ require 'dns-sd/resource_cache' require 'dns-sd/service' require 'dns-sd/target' require 'resolv' +require 'digest/md5' class DNSSD # A single instance of a service. # # This is where the rubber hits the road: servers to talk to, and instance @@ -91,15 +92,34 @@ # connection to the service instance, you call `#targets` again, both # because the DNS records may have expired (and thus will be re-queried), # but also because it'll ensure that the weight-based randomisation of the # server list is respected. # + # @param deterministic [String] (Optional) If provided, use the given string + # to create a seed to use when shuffling the records so that each time this + # method is called, the same set of records will be shuffled into the same + # order. By default, each time this method is called, records are (probably) + # returned in a different order. + # # @return [Array<DNSSD::Target>] - def targets + def targets(deterministic: nil) [].tap do |list| left = cached_resources(@fqdn, Resolv::DNS::Resource::IN::SRV) + seed = if deterministic + left = left.sort_by { |rr| [rr.target.to_s, rr.port] } + + Digest::MD5.hexdigest( + deterministic.to_s + + left.map { |rr| [rr.target.to_s, rr.port, rr.priority, rr.weight] }.inspect + ).hex + else + Random.new_seed + end + + prng = Random.new(seed) + # Happily, this algorithm, whilst a bit involved, maps quite directly # to the description from RFC2782, page 4, of which parts are quoted as # appropriate below. A practical example of how this process runs is # described in the test suite, also, which might help explain what's # happening. @@ -133,10 +153,10 @@ until candidates.empty? # > Compute the sum of the weights of those RRs, and with each RR # > associate the running sum in the selected order. Then choose a # > uniform random number between 0 and the sum computed # > (inclusive) - selector = rand(candidates.inject(1) { |n, rr| n + rr.weight }) + selector = prng.rand(candidates.inject(1) { |n, rr| n + rr.weight }) # > select the RR whose running sum value is the first in the # > selected order which is greater than or equal to the random # > number selected chosen = candidates.inject(0) do |n, rr|