Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,9 @@ TestResults/
*.msgpack
*-storage/
storage/data.msgpack
storage/root.msgpack
storage/root.msgpack

# macOS
.DS_Store
.AppleDouble
.LSOverride
38 changes: 38 additions & 0 deletions afs/blobstore/src/AfsStorageConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
"firestore" => CreateFirestoreConnector(configuration),
"azure.storage" => CreateAzureStorageConnector(configuration),
"s3" => CreateS3Connector(configuration),
"redis" => CreateRedisConnector(configuration),
_ => throw new NotSupportedException($"AFS storage type '{configuration.AfsStorageType}' is not supported")
};
}
Expand Down Expand Up @@ -333,6 +334,43 @@
}
}

/// <summary>
/// Creates a Redis connector.
/// </summary>
/// <param name="configuration">The storage configuration</param>
/// <returns>The Redis connector</returns>
private static IBlobStoreConnector CreateRedisConnector(IEmbeddedStorageConfiguration configuration)
{
try
{
// Use reflection to avoid hard dependency on Redis
var redisAssembly = System.Reflection.Assembly.LoadFrom("NebulaStore.Afs.Redis.dll");
var connectorType = redisAssembly.GetType("NebulaStore.Afs.Redis.RedisConnector");

if (connectorType == null)
throw new TypeLoadException("RedisConnector type not found");

var connectionString = configuration.AfsConnectionString ?? "localhost:6379";

// Use the factory method that takes connection string and database number
var factoryMethod = configuration.AfsUseCache
? connectorType.GetMethod("Caching", new[] { typeof(string), typeof(int) })
: connectorType.GetMethod("New", new[] { typeof(string), typeof(int) });

if (factoryMethod == null)
throw new MethodAccessException("RedisConnector factory method not found");

var connector = factoryMethod.Invoke(null, new object[] { connectionString, 0 });
return (IBlobStoreConnector)connector!;
}
catch (Exception ex) when (!(ex is ArgumentException))
{
throw new NotSupportedException(
"Redis connector could not be created. " +
"Make sure NebulaStore.Afs.Redis and StackExchange.Redis packages are installed.", ex);
}
}

/// <summary>
/// Registers type handlers for GigaMap serialization.
/// This enables automatic persistence of GigaMap instances following Eclipse Store patterns.
Expand Down Expand Up @@ -367,55 +405,55 @@
}

// IStorageConnection interface methods
public bool IssueGarbageCollection(TimeSpan timeBudget)

Check warning on line 408 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueGarbageCollection(TimeSpan)'

Check warning on line 408 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueGarbageCollection(TimeSpan)'
{
// AFS implementation would trigger GC with time budget
return true; // Return true if GC completed within time budget
}

public void IssueFullFileCheck()

Check warning on line 414 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFullFileCheck()'

Check warning on line 414 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFullFileCheck()'
{
// AFS implementation would check all files
}

public bool IssueFileCheck(TimeSpan timeBudget)

Check warning on line 419 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFileCheck(TimeSpan)'

Check warning on line 419 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFileCheck(TimeSpan)'
{
// AFS implementation would check files with time budget
return true; // Return true if file check completed within time budget
}

public void IssueFullCacheCheck()

Check warning on line 425 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFullCacheCheck()'

Check warning on line 425 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFullCacheCheck()'
{
// AFS implementation would check cache
}

public bool IssueCacheCheck(TimeSpan timeBudget)

Check warning on line 430 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueCacheCheck(TimeSpan)'

Check warning on line 430 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueCacheCheck(TimeSpan)'
{
// AFS implementation would check cache with time budget
return true; // Return true if cache check completed within time budget
}

public void IssueFullBackup(DirectoryInfo backupDirectory)

Check warning on line 436 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFullBackup(DirectoryInfo)'

Check warning on line 436 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueFullBackup(DirectoryInfo)'
{
// AFS implementation would backup to directory
}

public void IssueTransactionsLogCleanup()

Check warning on line 441 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueTransactionsLogCleanup()'

Check warning on line 441 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.IssueTransactionsLogCleanup()'
{
// AFS implementation would cleanup transaction logs
}

public IStorageStatistics CreateStorageStatistics()

Check warning on line 446 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.CreateStorageStatistics()'

Check warning on line 446 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.CreateStorageStatistics()'
{
return new AfsStorageStatistics(_configuration, _fileSystem);
}

public void ExportChannels(DirectoryInfo exportDirectory, bool performGarbageCollection)

Check warning on line 451 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.ExportChannels(DirectoryInfo, bool)'

Check warning on line 451 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.ExportChannels(DirectoryInfo, bool)'
{
// AFS implementation would export channels
}

public void ImportFiles(DirectoryInfo importDirectory)

Check warning on line 456 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (macos-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.ImportFiles(DirectoryInfo)'

Check warning on line 456 in afs/blobstore/src/AfsStorageConnection.cs

View workflow job for this annotation

GitHub Actions / build-matrix (ubuntu-latest, 9.0.x)

Missing XML comment for publicly visible type or member 'AfsStorageConnection.ImportFiles(DirectoryInfo)'
{
// AFS implementation would import files
}
Expand Down
36 changes: 36 additions & 0 deletions afs/redis/NebulaStore.Afs.Redis.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>NebulaStore.Afs.Redis</PackageId>
<PackageVersion>1.0.0</PackageVersion>
<Authors>NebulaStore Contributors</Authors>
<Description>Redis connector for NebulaStore Abstract File System (AFS)</Description>
<PackageTags>NebulaStore;AFS;Redis;Storage</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/hadv/NebulaStore</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateTargetFrameworkAttribute>false</GenerateTargetFrameworkAttribute>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\blobstore\NebulaStore.Afs.Blobstore.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
</ItemGroup>

<ItemGroup>
<Compile Remove="test/**" />
<EmbeddedResource Remove="test/**" />
<None Remove="test/**" />
</ItemGroup>

</Project>

262 changes: 262 additions & 0 deletions afs/redis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
# NebulaStore AFS Redis Connector

Redis connector for NebulaStore Abstract File System (AFS). This module provides a Redis-based storage backend for NebulaStore, allowing you to store object graphs in Redis using the StackExchange.Redis client.

## Overview

The Redis AFS connector stores files as numbered blob entries in Redis. Each blob is stored as a separate Redis key with binary data as the value. This implementation follows the Eclipse Store AFS pattern and is compatible with the NebulaStore storage system.

## Features

- **Redis Storage**: Store NebulaStore data in Redis key-value store
- **Caching Support**: Optional in-memory caching for improved performance
- **Configurable**: Flexible configuration options for connection, timeouts, and database selection
- **Thread-Safe**: All operations are thread-safe
- **Compatible**: Works seamlessly with NebulaStore's embedded storage system

## Installation

```bash
dotnet add package NebulaStore.Afs.Redis
dotnet add package StackExchange.Redis
```

## Quick Start

### Using with EmbeddedStorage

```csharp
using NebulaStore.Storage.Embedded;
using NebulaStore.Storage.EmbeddedConfiguration;

// Configure storage to use Redis
var config = EmbeddedStorageConfiguration.New()
.SetStorageDirectory("storage")
.SetAfsStorageType("redis")
.SetAfsConnectionString("localhost:6379")
.SetAfsUseCache(true);

// Start storage with Redis backend
using var storage = EmbeddedStorage.StartWithAfs(config);

// Use storage normally
var root = storage.Root<MyDataClass>();
root.SomeProperty = "value";
storage.StoreRoot();
```

### Direct AFS Usage

```csharp
using StackExchange.Redis;
using NebulaStore.Afs.Redis;
using NebulaStore.Afs.Blobstore;

// Create Redis connection
var redis = ConnectionMultiplexer.Connect("localhost:6379");

// Create connector
using var connector = RedisConnector.New(redis);

// Create file system
using var fileSystem = BlobStoreFileSystem.New(connector);

// Perform operations
var path = BlobStorePath.New("my-container", "folder", "file.dat");
var data = System.Text.Encoding.UTF8.GetBytes("Hello, Redis!");

fileSystem.IoHandler.WriteData(path, data);
var readData = fileSystem.IoHandler.ReadData(path, 0, -1);
```

### With Caching

```csharp
// Create connector with caching enabled
using var connector = RedisConnector.Caching("localhost:6379");
using var fileSystem = BlobStoreFileSystem.New(connector);
```

### Using RedisConfiguration

```csharp
using StackExchange.Redis;
using NebulaStore.Afs.Redis;

// Create configuration
var config = RedisConfiguration.New()
.SetConnectionString("localhost:6379")
.SetDatabaseNumber(0)
.SetUseCache(true)
.SetCommandTimeout(TimeSpan.FromMinutes(1))
.SetPassword("your-password") // Optional
.SetUseSsl(false); // Optional

// Build StackExchange.Redis options
var options = config.ToConfigurationOptions();
var redis = ConnectionMultiplexer.Connect(options);

// Create connector
using var connector = RedisConnector.New(redis);
```

## Configuration Options

### RedisConfiguration Properties

- **ConnectionString**: Redis connection string (default: "localhost:6379")
- **DatabaseNumber**: Redis database number (default: 0)
- **UseCache**: Enable in-memory caching (default: true)
- **CommandTimeout**: Command execution timeout (default: 1 minute)
- **ConnectTimeout**: Connection timeout (default: 5 seconds)
- **SyncTimeout**: Synchronous operation timeout (default: 5 seconds)
- **AllowAdmin**: Allow admin operations (default: true)
- **AbortOnConnectFail**: Abort on connection failure (default: false)
- **Password**: Redis authentication password (optional)
- **UseSsl**: Enable SSL/TLS (default: false)

### AFS Configuration

When using with EmbeddedStorage, configure via `IEmbeddedStorageConfiguration`:

```csharp
var config = EmbeddedStorageConfiguration.New()
.SetAfsStorageType("redis")
.SetAfsConnectionString("localhost:6379")
.SetAfsUseCache(true);
```

## Architecture

### Storage Structure

Files are stored in Redis using a hierarchical key structure:

```
container/path/to/file.0
container/path/to/file.1
container/path/to/file.2
```

Each file is split into numbered blobs (suffixed with `.0`, `.1`, `.2`, etc.). This allows for efficient storage and retrieval of large files.

### Key Features

1. **Virtual Directories**: Directories are virtual in Redis - they don't need to be explicitly created
2. **Blob Numbering**: Files are split into numbered blobs for efficient storage
3. **Atomic Operations**: Uses Redis atomic operations for data consistency
4. **Caching**: Optional in-memory caching reduces Redis queries

## Performance Considerations

- **Caching**: Enable caching for read-heavy workloads
- **Connection Pooling**: StackExchange.Redis handles connection pooling automatically
- **Database Selection**: Use different database numbers to isolate data
- **Timeouts**: Adjust timeouts based on your network latency and data size

## Requirements

- .NET 9.0 or later
- StackExchange.Redis 2.8.16 or later
- Redis server 5.0 or later (recommended)

## Thread Safety

All operations in the RedisConnector are thread-safe. The connector uses:
- Lock-based synchronization for cache operations
- StackExchange.Redis's built-in thread-safe operations
- Atomic Redis commands where applicable

## Error Handling

The connector handles common Redis errors:
- Connection failures
- Timeout errors
- Key not found scenarios
- Data serialization issues

## Limitations

- Redis key size limits apply (512 MB per key)
- Memory constraints of Redis server
- Network latency affects performance
- Requires Redis server to be running and accessible

## Examples

### Complete Example with Custom Data

```csharp
using StackExchange.Redis;
using NebulaStore.Afs.Redis;
using NebulaStore.Afs.Blobstore;
using NebulaStore.Storage.Embedded;

// Define your data model
public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "";
public List<Order> Orders { get; set; } = new();
}

public class Order
{
public int OrderId { get; set; }
public decimal Amount { get; set; }
}

// Configure and use Redis storage
var config = EmbeddedStorageConfiguration.New()
.SetStorageDirectory("redis-storage")
.SetAfsStorageType("redis")
.SetAfsConnectionString("localhost:6379")
.SetAfsUseCache(true);

using var storage = EmbeddedStorage.StartWithAfs(config);

// Create and store data
var root = storage.Root<Customer>();
root.Id = 1;
root.Name = "John Doe";
root.Orders.Add(new Order { OrderId = 100, Amount = 99.99m });

storage.StoreRoot();
```

## Troubleshooting

### Connection Issues

If you encounter connection issues:
1. Verify Redis server is running: `redis-cli ping`
2. Check connection string format
3. Verify network connectivity
4. Check firewall settings

### Performance Issues

For performance optimization:
1. Enable caching for read-heavy workloads
2. Adjust command timeouts
3. Use connection multiplexing
4. Monitor Redis memory usage

## License

This project is licensed under the MIT License.

## Contributing

Contributions are welcome! Please ensure:
- Code follows existing patterns
- Tests are included
- Documentation is updated

## Related

- [NebulaStore](../../README.md)
- [AFS Overview](../README.md)
- [Eclipse Store](https://github.com/eclipse-store/store)
- [StackExchange.Redis](https://github.com/StackExchange/StackExchange.Redis)

Loading
Loading