Class: WSDL::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/wsdl/cache.rb

Overview

Thread-safe in-memory LRU cache for parsed WSDL definitions.

This cache is designed to avoid redundant HTTP requests and parsing when working with WSDL documents, especially in multithreaded environments. When a +max_entries+ limit is set, the least recently used entry is evicted to make room for new ones.

Uses double-checked locking so the global mutex is never held while the block executes. Concurrent requests for different keys compute in parallel. Under contention for the same uncached key, duplicate computation may occur but only the first result is stored — the block must be idempotent.

Examples:

Basic usage with default settings

cache = WSDL::Cache.new
value = cache.fetch('http://example.com/service?wsdl') { expensive_operation }

With TTL (time-to-live)

cache = WSDL::Cache.new(ttl: 3600)  # 1 hour TTL

With max entries to prevent unbounded memory growth

cache = WSDL::Cache.new(max_entries: 100)

Custom cache implementation for Redis

class RedisCache
  def initialize(redis, ttl: nil)
    @redis = redis
    @ttl = ttl
  end

  def fetch(key)
    cached = @redis.get(key)
    return Marshal.load(cached) if cached

    value = yield
    @redis.set(key, Marshal.dump(value), ex: @ttl)
    value
  end

  def clear
    @redis.flushdb
  end
end

WSDL.cache = RedisCache.new(Redis.new, ttl: 3600)

Instance Method Summary collapse

Constructor Details

#initialize(ttl: nil, max_entries: nil) ⇒ Cache

Creates a new Cache instance.



58
59
60
61
62
63
# File 'lib/wsdl/cache.rb', line 58

def initialize(ttl: nil, max_entries: nil)
  @store = {}
  @ttl = ttl
  @max_entries = max_entries
  @mutex = Mutex.new
end

Instance Method Details

#clearvoid

This method returns an undefined value.

Removes all entries from the cache.



102
103
104
105
106
# File 'lib/wsdl/cache.rb', line 102

def clear
  @mutex.synchronize do
    @store.clear
  end
end

#delete(key) ⇒ Object?

Removes a specific entry from the cache.



132
133
134
135
136
137
# File 'lib/wsdl/cache.rb', line 132

def delete(key)
  @mutex.synchronize do
    entry = @store.delete(key)
    entry&.fetch(:value)
  end
end

#fetch(key) { ... } ⇒ Object

Fetches a value from the cache, or computes and stores it if not present.

If the key exists and hasn't expired, returns the cached value. Otherwise, yields to the block outside the lock, stores the result, and returns it. A second lock acquisition double-checks that another thread hasn't populated the entry in the meantime.

Yields:

  • computes the value if not cached

Yield Returns:

  • (Object)

    the value to cache



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/wsdl/cache.rb', line 76

def fetch(key)
  # Fast path — return a cached, non-expired entry.
  @mutex.synchronize do
    entry = promote!(key)
    return entry[:value] if entry
  end

  # Compute outside the lock so concurrent misses for different keys
  # are not serialized against each other.
  value = yield

  # Store the result.  Double-check: if another thread populated
  # the entry while we were computing, keep the earlier value.
  @mutex.synchronize do
    entry = promote!(key)
    return entry[:value] if entry

    evict_lru! if @max_entries && @store.size >= @max_entries
    @store[key] = { value:, timestamp: Time.now }
    value
  end
end

#key?(key) ⇒ Boolean

Checks if a key exists in the cache and hasn't expired.



121
122
123
124
125
126
# File 'lib/wsdl/cache.rb', line 121

def key?(key)
  @mutex.synchronize do
    entry = @store[key]
    !!(entry && !expired?(entry))
  end
end

#sizeInteger

Returns the number of entries in the cache.



111
112
113
114
115
# File 'lib/wsdl/cache.rb', line 111

def size
  @mutex.synchronize do
    @store.size
  end
end