solana-shardmap
A generic, efficient shard-based mapping primitive for Solana programs that enables horizontal scaling of key-value storage across multiple accounts.
๐ Why solana-shardmap?
Solana programs face unique storage challenges:
- No native HashMap: Unlike EVM smart contracts, Solana doesn't provide built-in mapping types
- Account size limits: Individual accounts are limited to ~10KB, constraining large datasets
- Performance bottlenecks: Large single-account storage becomes inefficient
- Horizontal scaling: Need to distribute data across multiple accounts (PDAs) for scalability
solana-shardmap solves these problems by providing a production-ready, generic sharding solution that's fully compatible with the Anchor framework.
โจ Features
- ๐ง Generic Design: Works with any
AnchorSerialize + AnchorDeserializetypes - ๐ Batch Operations: Efficient bulk insert, get, and remove operations
- ๐ Capacity Management: Built-in monitoring and optimization tools
- ๐๏ธ PDA Helpers: Utilities for deterministic shard account derivation
- โก Performance: Optimized for Solana's account-based architecture
- ๐งช Well Tested: Comprehensive test suite covering all functionality
- ๐ Anchor Compatible: Seamless integration with Anchor programs
๐ฆ Installation
Add to your Cargo.toml:
[]
= "0.1.0"
= "0.30.1"
๐ Quick Start
Basic Usage
use ;
use Pubkey;
// Create a new shard with capacity for 1000 items
let mut shard = new;
// Insert key-value pairs
let user = new_unique;
shard.insert.unwrap;
// Retrieve values
assert_eq!;
// Check capacity and utilization
println!;
println!;
Batch Operations
use ;
let mut shard = new;
// Efficient batch insertion
let items = vec!;
let results = shard.insert_batch.unwrap;
assert!;
// Batch retrieval
let keys = ;
let values = shard.get_batch;
// values = [Some("Alice"), Some("Bob"), Some("Charlie"), None]
๐ง Anchor Program Integration
1. Define Your Account Structure
use *;
use ;
// Wrapper account for your shard
2. Initialize Shards
3. Client-Side Integration (TypeScript)
import { Program, web3 } from "@coral-xyz/anchor";
class ShardManager {
constructor(private program: Program, private totalShards: number = 10) {}
// Distribute users across shards using hash-based selection
getUserShardIndex(userPubkey: web3.PublicKey): number {
const hash = require("crypto")
.createHash("sha256")
.update(userPubkey.toBuffer())
.digest();
return hash.readUInt32BE(0) % this.totalShards;
}
getShardPDA(shardIndex: number): [web3.PublicKey, number] {
return web3.PublicKey.findProgramAddressSync(
[Buffer.from("profile_shard"), Buffer.from([shardIndex])],
this.program.programId
);
}
async createProfile(
user: web3.Keypair,
username: string,
level: number,
score: number
): Promise<string> {
const shardIndex = this.getUserShardIndex(user.publicKey);
const [shardPDA] = this.getShardPDA(shardIndex);
return await this.program.methods
.upsertProfile(username, level, score)
.accounts({
profileShard: shardPDA,
user: user.publicKey,
})
.signers([user])
.rpc();
}
}
๐ Advanced Features
Capacity Management
// Monitor shard health
let stats = shard.capacity_stats;
println!;
println!;
// Check if near capacity
if shard.is_near_capacity
// Optimize memory usage
shard.shrink_to_fit;
shard.reserve; // Pre-allocate for 50 more items
Batch Operations for Better Performance
// Instead of individual operations:
// for item in items { shard.insert(item.0, item.1)?; } // โ Inefficient
// Use batch operations:
let results = shard.insert_batch?; // โ
Efficient
// Check if batch would succeed before attempting
if shard.can_insert_batch
๐๏ธ Architecture Patterns
Simple Hash-Based Sharding
use derive_shard_pda;
Dynamic Shard Management
// Monitor and rebalance shards
async
๐ API Reference
Core Types
MappingShard<K, V>: The main shard structureShardedMap<K, V>: Trait defining core operationsShardKey: Trait bound for keys (AnchorSerialize + AnchorDeserialize + Clone + PartialEq + Debug)ShardValue: Trait bound for values (AnchorSerialize + AnchorDeserialize + Clone)
Key Methods
| Method | Description |
|---|---|
new(id, capacity) |
Create a new shard |
insert(key, value) |
Insert or update a key-value pair |
get(key) |
Retrieve a value by key |
remove(key) |
Remove a key-value pair |
insert_batch(items) |
Batch insert operation |
get_batch(keys) |
Batch retrieval operation |
utilization_percentage() |
Get current utilization |
remaining_capacity() |
Get available space |
is_full() / is_empty() |
Check shard state |
๐งช Testing
Run the test suite:
Run with verbose output:
Check formatting and linting:
๐ Performance Considerations
- Shard Size: Aim for 50-200 items per shard for optimal performance
- Batch Operations: Always prefer batch operations for multiple items
- Memory Management: Use
shrink_to_fit()andreserve()for memory optimization - Monitoring: Regularly check utilization to know when to create new shards
๐ค 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.
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Built for the Solana ecosystem
- Inspired by the need for scalable storage solutions
- Compatible with the Anchor framework
Made with โค๏ธ for the Solana developer community