README
ยถ
๐ MemoryStore
MemoryStore is a high-performance, thread-safe, in-memory key-value store implemented in Go. It features automatic key expiration, JSON serialization support, batch operations, metrics, and an agnostic Publish/Subscribe system (supporting In-Memory and Google Cloud PubSub).
Features
- ๐ Thread-safe operations
- โฐ Automatic key expiration
- ๐งน Background cleanup of expired keys
- ๐ฆ Support for both raw bytes and JSON data
- ๐ช High-performance using go-json
- ๐ Batch operations (
SetMulti,GetMulti) - ๐ Built-in Metrics and Monitoring
- ๐ก Agnostic Publish/Subscribe system (In-Memory & GCP PubSub)
- ๐ Clean shutdown mechanism
- ๐ Comprehensive documentation
Installation
go get github.com/BryceWayne/MemoryStore
Quick Start
Here's a simple example demonstrating basic usage:
package main
import (
"log"
"time"
"github.com/BryceWayne/MemoryStore/memorystore"
)
func main() {
// Create a new store instance (defaults to In-Memory PubSub)
store := memorystore.NewMemoryStore()
defer store.Stop()
// Store a string (converted to bytes)
err := store.Set("greeting", []byte("Hello, World!"), 1*time.Minute)
if err != nil {
log.Fatal(err)
}
// Retrieve the value
if value, exists := store.Get("greeting"); exists {
log.Printf("Value: %s", string(value))
}
}
PubSub Usage
MemoryStore supports an agnostic PubSub interface. By default, it uses an in-memory implementation. To use Google Cloud PubSub, simply provide your Project ID configuration.
Using In-Memory PubSub (Default)
package main
import (
"log"
"time"
"github.com/BryceWayne/MemoryStore/memorystore"
)
func main() {
store := memorystore.NewMemoryStore()
defer store.Stop()
// Subscribe to a topic
msgs, err := store.Subscribe("user-updates")
if err != nil {
log.Fatal(err)
}
// Listen in background
go func() {
for msg := range msgs {
log.Printf("Received: %s", string(msg))
}
}()
// Publish to the topic
err = store.Publish("user-updates", []byte("User 123 logged in"))
if err != nil {
log.Fatal(err)
}
// Give time for message delivery
time.Sleep(1 * time.Second)
}
Using Google Cloud PubSub
package main
import (
"log"
"time"
"github.com/BryceWayne/MemoryStore/memorystore"
)
func main() {
// Configure with GCP Project ID
config := memorystore.Config{
GCPProjectID: "my-gcp-project-id",
}
store := memorystore.NewMemoryStoreWithConfig(config)
defer store.Stop()
// Subscribe to a topic
// Note: GCP PubSub creates a subscription for this topic
msgs, err := store.Subscribe("user-updates")
if err != nil {
log.Fatal(err)
}
// Listen in background
go func() {
for msg := range msgs {
log.Printf("Received: %s", string(msg))
}
}()
// Publish to the topic
err = store.Publish("user-updates", []byte("User 123 logged in"))
if err != nil {
log.Fatal(err)
}
// Give time for message delivery
time.Sleep(1 * time.Second)
}
Or simply set GOOGLE_CLOUD_PROJECT environment variable:
export GOOGLE_CLOUD_PROJECT=my-project-id
store := memorystore.NewMemoryStore() // Automatically picks up GCP PubSub
Advanced Usage
Working with JSON
MemoryStore provides convenient methods for JSON serialization:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
store := memorystore.NewMemoryStore()
defer store.Stop()
// Store JSON data
user := User{Name: "Alice", Email: "[email protected]"}
err := store.SetJSON("user:123", user, 1*time.Hour)
if err != nil {
log.Fatal(err)
}
// Retrieve JSON data
var retrievedUser User
exists, err := store.GetJSON("user:123", &retrievedUser)
if err != nil {
log.Fatal(err)
}
if exists {
log.Printf("User: %+v", retrievedUser)
}
}
Batch Operations
Efficiently set or get multiple items at once:
items := map[string][]byte{
"key1": []byte("val1"),
"key2": []byte("val2"),
}
store.SetMulti(items, time.Minute)
results := store.GetMulti([]string{"key1", "key2"})
Metrics
Monitor cache performance:
metrics := store.GetMetrics()
log.Printf("Hits: %d, Misses: %d, Items: %d", metrics.Hits, metrics.Misses, metrics.Items)
Expiration and Cleanup
Keys automatically expire after their specified duration:
// Set a value that expires in 5 seconds
store.Set("temp", []byte("temporary value"), 5*time.Second)
// The background cleanup routine will automatically remove expired items
// You can also manually check if an item exists
time.Sleep(6 * time.Second)
if _, exists := store.Get("temp"); !exists {
log.Println("Key has expired")
}
Proper Shutdown
Always ensure proper cleanup by calling Stop():
store := memorystore.NewMemoryStore()
defer func() {
if err := store.Stop(); err != nil {
log.Printf("Error stopping store: %v", err)
}
}()
Architecture
Here's an overview of how MemoryStore is architected:
graph TD
subgraph Client Layer
User[User Application]
end
subgraph MemoryStore System
API[MemoryStore API]
subgraph "Storage Engine"
Shards[Sharded Storage Map]
Metrics[Metrics Collector]
end
subgraph "Background Services"
Cleaner[Cleanup Routine]
end
subgraph "Pub/Sub System"
PS_Interface[PubSub Interface]
PS_Mem[In-Memory Broker]
PS_GCP[Google Cloud PubSub]
end
end
User -->|Set/Get| API
User -->|Subscribe| API
API -->|Read/Write| Shards
API -->|Record| Metrics
API -->|Publish| PS_Interface
Cleaner -->|Periodically Scan| Shards
Cleaner -->|Remove Expired| Shards
PS_Interface -->|Default| PS_Mem
PS_Interface -->|Configured| PS_GCP
PubSub Data Flow
sequenceDiagram
participant P as Publisher
participant MS as MemoryStore
participant S as Subscriber
S->>MS: Subscribe("topic-A")
activate MS
MS-->>S: Returns Channel
deactivate MS
P->>MS: Publish("topic-A", "payload")
activate MS
MS->>S: Sends "payload" to Channel
deactivate MS
Performance Considerations
- Uses
github.com/goccy/go-jsonfor faster JSON operations - Minimizes lock contention with RWMutex
- Efficient background cleanup of expired items
- Memory-efficient storage using byte slices
Building and Testing
Use the provided Makefile:
# Build the project
make build
# Run tests (with race detection)
make test
# Run benchmarks
make bench
# Run linter
make lint
# Format code
make fmt
# Show all targets
make help
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Thanks to the Go team for the amazing standard library
- go-json for high-performance JSON operations
Support
If you have any questions or need help integrating MemoryStore, please:
- Check the documentation
- Open an issue with a detailed description
- Reach out through the discussions tab
Documentation
ยถ
Overview ยถ
main.go Package main provides a demonstration of the memorystore package functionality.
Directories
ยถ
| Path | Synopsis |
|---|---|
|
memorystore/memorystore.go Package memorystore provides a simple in-memory cache implementation with automatic cleanup of expired items.
|
memorystore/memorystore.go Package memorystore provides a simple in-memory cache implementation with automatic cleanup of expired items. |