# ExCache
ExCache is a simple, yet powerful caching framework for Elixir applications. It provides a fast, in-memory caching solution with TTL (Time To Live) support, comprehensive statistics, and a clean, idiomatic API.
## Features
- **Fast In-Memory Caching**: Built on ETS (Erlang Term Storage) for optimal performance
- **TTL Support**: Automatic expiration of cached entries with configurable time-to-live
- **Comprehensive Statistics**: Real-time tracking of cache hits, misses, and operations
- **Manual Cleanup**: On-demand cleanup of expired entries in addition to automatic cleanup
- **OTP Integration**: Full GenServer-based implementation with supervisor support
- **Concurrent Access**: Thread-safe operations with excellent performance under load
- **Flexible API**: Simple put/get/delete operations with powerful fallback functionality
- **Idiomatic Elixir**: Follows Elixir conventions and best practices
## Installation
Add `ex_cache` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:ex_cache, "~> 0.2.0"}
]
end
```
Then run `mix deps.get` to install the dependency.
## Usage
### Basic Usage
```elixir
# Start a cache process
{:ok, pid} = ExCache.Ets.start_link(:my_cache)
# Store a value
:ok = ExCache.Ets.put(:my_cache, :user_123, %{name: "John", email: "john@example.com"})
# Retrieve a value
user = ExCache.Ets.get(:my_cache, :user_123)
# => %{name: "John", email: "john@example.com"}
# Store with TTL (in milliseconds)
:ok = ExCache.Ets.put(:my_cache, :session_token, "abc123", ttl: 3600000) # 1 hour
# Delete a value
:ok = ExCache.Ets.del(:my_cache, :user_123)
```
### Using Fetch with Fallback
The `fetch/4` function provides a powerful pattern for retrieving values with fallback logic:
```elixir
# Fetch with automatic caching of computed value
user = ExCache.Ets.fetch(:my_cache, :user_456, [ttl: 300000], fn user_id ->
# This function is only called if the key is not in cache
{:commit, Database.get_user(user_id)} # Store the result in cache
end)
# Fetch without caching (ignore pattern)
config = ExCache.Ets.fetch(:my_cache, :app_config, [], fn key ->
{:ignore, File.read!("config/#{key}.json")} # Return without storing
end)
```
### Supervisor Integration
ExCache is designed to work seamlessly with OTP supervisors:
```elixir
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
{ExCache.Ets, name: :my_cache},
# ... other children
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
end
```
### Cache Statistics
Monitor cache performance with built-in statistics:
```elixir
# Get current statistics
stats = ExCache.Ets.stats(:my_cache)
# => %{
# hits: 1250,
# misses: 42,
# puts: 1320,
# deletes: 89,
# total_operations: 2701
# }
# Calculate hit rate
hit_rate = stats.hits / (stats.hits + stats.misses)
# => 0.9675 (96.75% hit rate)
# Reset statistics when needed
:ok = ExCache.Ets.reset_stats(:my_cache)
```
### Manual Cleanup
While ExCache automatically cleans up expired entries, you can also trigger manual cleanup:
```elixir
# Manually clean up expired entries
{:ok, deleted_count} = ExCache.Ets.cleanup_expired(:my_cache)
IO.puts("Cleaned up #{deleted_count} expired entries")
```
## API Reference
### Core Functions
#### `put(name, key, value, opts \\ [])`
Store a key-value pair in the cache.
- **Parameters**:
- `name`: Cache process name or PID
- `key`: Any term to use as the key
- `value`: Any term to store as the value
- `opts`: Keyword list of options
- **Options**:
- `:ttl` - Time to live in milliseconds (default: `:infinity`)
- **Returns**: `:ok`
#### `get(name, key)`
Retrieve a value from the cache.
- **Parameters**:
- `name`: Cache process name or PID
- `key`: Key to retrieve
- **Returns**: `value | nil` (returns `nil` if key doesn't exist or has expired)
#### `del(name, key)`
Delete a key-value pair from the cache.
- **Parameters**:
- `name`: Cache process name or PID
- `key`: Key to delete
- **Returns**: `:ok`
### Advanced Functions
#### `fetch(name, key, opts \\ [], fallback)`
Retrieve a value with fallback functionality.
- **Parameters**:
- `name`: Cache process name or PID
- `key`: Key to retrieve
- `opts`: Keyword list of options
- `fallback`: Function to call if key is not found
- **Options**:
- `:ttl` - Time to live for newly cached values (default: `:infinity`)
- **Fallback Function**: Should return either:
- `{:commit, value}` - Store the value in cache and return it
- `{:ignore, value}` - Return the value without storing it
- **Returns**: The cached or computed value
### Statistics Functions
#### `stats(name)`
Get cache statistics.
- **Parameters**:
- `name`: Cache process name or PID
- **Returns**: Map containing:
- `:hits` - Number of cache hits
- `:misses` - Number of cache misses
- `:puts` - Number of put operations
- `:deletes` - Number of delete operations
- `:total_operations` - Total number of operations
#### `reset_stats(name)`
Reset all cache statistics to zero.
- **Parameters**:
- `name`: Cache process name or PID
- **Returns**: `:ok`
### Maintenance Functions
#### `cleanup_expired(name)`
Manually clean up expired entries.
- **Parameters**:
- `name`: Cache process name or PID
- **Returns**: `{:ok, deleted_count}` where `deleted_count` is the number of entries deleted
## Configuration
### Cache Process Configuration
The cache process uses a timeout of 5000 milliseconds for automatic cleanup of expired entries. This is currently not configurable but provides a good balance between cleanup frequency and performance.
### TTL Management
- **Infinite TTL**: Use `ttl: :infinity` for entries that should never expire
- **Short TTL**: Values like `ttl: 1000` (1 second) for temporary data
- **Long TTL**: Values like `ttl: 86400000` (24 hours) for long-term caching
### Error Handling
ExCache gracefully handles various edge cases:
- **Invalid TTL Values**: Negative, zero, or invalid TTL values are treated as `:infinity`
- **Missing Keys**: `get/3` and `del/3` operations on non-existent keys are safe and return appropriate values
- **Concurrent Access**: All operations are thread-safe and designed for concurrent use
## Performance Considerations
### Memory Usage
ExCache uses ETS tables for storage, which are kept in memory. Monitor memory usage when caching large amounts of data.
### Automatic Cleanup
Expired entries are automatically cleaned up every 5 seconds. This process is efficient and doesn't block normal operations.
### Concurrency
The cache is designed for high concurrency with minimal contention between operations. All read operations are synchronous, while write operations are asynchronous.
## Testing
Run the test suite:
```bash
mix test
```
Include property tests:
```bash
mix test test/ex_cache_property_test.exs
```
## Contributing
We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details.
### Development Setup
1. Fork the repository
2. Clone your fork: `git clone https://github.com/your-username/ex_cache.git`
3. Create your feature branch: `git checkout -b feature/amazing-feature`
4. Install dependencies: `mix deps.get`
5. Run tests: `mix test`
6. Commit your changes: `git commit -m 'Add amazing feature'`
7. Push to the branch: `git push origin feature/amazing-feature`
8. Open a Pull Request
### Code Style
- Follow the existing code style
- Ensure all tests pass
- Add tests for new functionality
- Update documentation as needed
## License
ExCache is released under the [MIT License](LICENSE).
## Changelog
### v0.2.0 (2024-01-XX)
#### Added
- Comprehensive statistics tracking for cache operations
- Manual cleanup functionality for expired entries
- Enhanced TTL management with graceful error handling
- Extensive test suite including property-based tests
- Complete API documentation and usage examples
#### Changed
- Improved error handling for invalid TTL values
- Optimized cleanup algorithm for better performance
- Enhanced concurrent operation support
#### Fixed
- API return value consistency across all operations
- TTL expiration edge cases
- Memory leaks in cleanup process
### v0.1.1 (2024-01-XX)
- Initial release
- Basic ETS-based caching implementation
- TTL support
- Core API functions