Created
May 8, 2025 08:15
-
-
Save casperlehmann/e442ecc4186f325c1b5ac5eca7894213 to your computer and use it in GitHub Desktop.
ruby-twin-keys-cache.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env ruby | |
| def mutate_hash_key_and_return_cache | |
| key1 = { k: 1 } | |
| key2 = { k: 2 } | |
| puts "key hash: #{key1.hash} obj_id: #{key1.object_id}" # key hash: 2866644869520814412 obj_id: 60 | |
| puts "val hash: #{key2.hash} obj_id: #{key2.object_id}" # val hash: -3505864355511529673 obj_id: 80 | |
| cache = { key1 => "x", key2 => "y" } | |
| key2[:k] = 1 # The two keys in the cache now have the same value. | |
| puts cache # {{:k=>1}=>"x", {:k=>1}=>"y"} | |
| puts "--------------------------------" | |
| # Returning cache object. Original references to objects key1 and key2 will now go out of scope. | |
| # References still exist in the cache object. But you won't be able to access them without iterating through the | |
| # cache, since this is the only way to get the key with the correct object id. | |
| # | |
| # Note: The hash values of the keys are identical. | |
| # But each object id remain unique (and unchanging), even as the values are updated. | |
| # Now, will these object ids when the process is reforked? Will new objects be created from scratch, and will these | |
| # previously distinct objects share the same identity? | |
| # If so, then we might have lost object permanence, since previously distinct cache keys are now one, and can only | |
| # point to one of the two cache values | |
| cache | |
| end | |
| def print_cache_details(cache) | |
| cache.each_pair do |key, val| | |
| puts "Cache object ID: #{cache.object_id}. Contents:" | |
| puts "key: %s val: %s" % [key, val] | |
| puts "key hash: #{key.hash} obj_id: #{key.object_id}" | |
| puts "val hash: #{val.hash} obj_id: #{val.object_id}" | |
| end | |
| puts "--------------------------------" | |
| end | |
| def monitor_cache_periodically(cache) | |
| loop do | |
| threads = 10.times.map do | |
| Thread.new do | |
| 10_000.times { "string" * 1000 } | |
| end | |
| end | |
| threads.each(&:join) | |
| puts "Start garbage collection manually" | |
| GC.start | |
| print_cache_details(cache) | |
| begin | |
| puts "ObjectSpace._id2ref(60): #{ObjectSpace._id2ref(60)} ObjectSpace._id2ref(80): #{ObjectSpace._id2ref(80)}" | |
| rescue RangeError | |
| puts "A key has been garbage collected" | |
| end | |
| sleep 30 | |
| end | |
| end | |
| cache = mutate_hash_key_and_return_cache | |
| monitor_cache_periodically(cache) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment