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
17 changes: 17 additions & 0 deletions NebulaStore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaStore.GigaMap", "giga
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaStore.GigaMap.Tests", "gigamap\tests\NebulaStore.GigaMap.Tests.csproj", "{I8J9K0L1-M2N3-4567-OPQR-ST8901234567}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aws", "aws", "{J9K0L1M2-N3O4-5678-PQRS-TU9012345678}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaStore.Afs.Aws.S3", "afs\aws\s3\NebulaStore.Afs.Aws.S3.csproj", "{K0L1M2N3-O4P5-6789-QRST-UV0123456789}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NebulaStore.Afs.Aws.S3.Tests", "afs\aws\s3\test\NebulaStore.Afs.Aws.S3.Tests.csproj", "{L1M2N3O4-P5Q6-7890-RSTU-VW1234567890}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -78,6 +84,14 @@ Global
{I8J9K0L1-M2N3-4567-OPQR-ST8901234567}.Debug|Any CPU.Build.0 = Debug|Any CPU
{I8J9K0L1-M2N3-4567-OPQR-ST8901234567}.Release|Any CPU.ActiveCfg = Release|Any CPU
{I8J9K0L1-M2N3-4567-OPQR-ST8901234567}.Release|Any CPU.Build.0 = Release|Any CPU
{K0L1M2N3-O4P5-6789-QRST-UV0123456789}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{K0L1M2N3-O4P5-6789-QRST-UV0123456789}.Debug|Any CPU.Build.0 = Debug|Any CPU
{K0L1M2N3-O4P5-6789-QRST-UV0123456789}.Release|Any CPU.ActiveCfg = Release|Any CPU
{K0L1M2N3-O4P5-6789-QRST-UV0123456789}.Release|Any CPU.Build.0 = Release|Any CPU
{L1M2N3O4-P5Q6-7890-RSTU-VW1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{L1M2N3O4-P5Q6-7890-RSTU-VW1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
{L1M2N3O4-P5Q6-7890-RSTU-VW1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
{L1M2N3O4-P5Q6-7890-RSTU-VW1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {D0E78B5A-0D39-4CE8-A836-5FC8C7D60478}
Expand All @@ -88,5 +102,8 @@ Global
{F5G6H7I8-J9K0-1234-LMNO-PQ5678901234} = {F6G7H8I9-J0K1-2345-MNOP-QR6789012345}
{G6H7I8J9-K0L1-2345-NOPQ-RS6789012345} = {F6G7H8I9-J0K1-2345-MNOP-QR6789012345}
{2099EB97-32CB-4403-BF84-AD7F965FA520} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
{J9K0L1M2-N3O4-5678-PQRS-TU9012345678} = {F6G7H8I9-J0K1-2345-MNOP-QR6789012345}
{K0L1M2N3-O4P5-6789-QRST-UV0123456789} = {J9K0L1M2-N3O4-5678-PQRS-TU9012345678}
{L1M2N3O4-P5Q6-7890-RSTU-VW1234567890} = {J9K0L1M2-N3O4-5678-PQRS-TU9012345678}
EndGlobalSection
EndGlobal
50 changes: 49 additions & 1 deletion afs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,60 @@ var config = EmbeddedStorageConfiguration.New()
- Proper authentication (service account key, application default credentials, etc.)
- `Google.Cloud.Firestore` NuGet package

### AWS S3 Storage

**Type**: `"s3"`

AWS S3 provides scalable object storage with global availability:

```csharp
using Amazon;
using Amazon.S3;
using NebulaStore.Afs.Aws.S3;

// Create S3 client
var s3Client = new AmazonS3Client("access-key", "secret-key", RegionEndpoint.USEast1);

// Create S3 configuration
var s3Config = AwsS3Configuration.New()
.SetCredentials("access-key", "secret-key")
.SetRegion(RegionEndpoint.USEast1)
.SetUseCache(true);

// Use with embedded storage
var config = EmbeddedStorageConfiguration.New()
.SetStorageDirectory("s3-storage")
.SetUseAfs(true)
.SetAfsStorageType("s3")
.SetAfsConnectionString("my-bucket-name")
.Build();

using var s3Storage = EmbeddedStorage.StartWithAfs(config);

// Direct S3 connector usage
using var connector = AwsS3Connector.New(s3Client, s3Config);
using var fileSystem = BlobStoreFileSystem.New(connector);
```

**Requirements:**
- AWS account with S3 access
- Proper authentication (access keys, IAM roles, etc.)
- `AWSSDK.S3` NuGet package
- S3 bucket created for storage

**Features:**
- Unlimited storage capacity
- Global availability and durability
- Multiple storage classes for cost optimization
- Server-side encryption support
- S3-compatible services support (MinIO, etc.)

### Future Storage Types

The AFS architecture supports additional storage backends:
- **NIO**: Java NIO-based file operations
- **SQL**: Database-backed storage
- **Cloud**: AWS S3, Azure Blob, Google Cloud Storage
- **Azure Blob**: Microsoft Azure Blob Storage
- **Redis**: In-memory storage with persistence
- **Custom**: Implement `IBlobStoreConnector` for custom backends

Expand Down
37 changes: 37 additions & 0 deletions afs/aws/s3/NebulaStore.Afs.Aws.S3.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsAsErrors />
<WarningsNotAsErrors>CS1591;NU1603</WarningsNotAsErrors>
</PropertyGroup>

<PropertyGroup>
<AssemblyTitle>NebulaStore Abstract File System AWS S3</AssemblyTitle>
<AssemblyDescription>AWS S3 adapter for NebulaStore Abstract File System</AssemblyDescription>
<AssemblyCompany>NebulaStore</AssemblyCompany>
<AssemblyProduct>NebulaStore</AssemblyProduct>
<Copyright>Copyright © NebulaStore 2025</Copyright>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<RootNamespace>NebulaStore.Afs.Aws.S3</RootNamespace>
</PropertyGroup>

<ItemGroup>
<Compile Remove="test/**/*.cs" />
</ItemGroup>

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

<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.401" />
</ItemGroup>

</Project>
228 changes: 228 additions & 0 deletions afs/aws/s3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# AFS adapter for AWS S3

This module provides an Abstract File System (AFS) adapter for Amazon S3, allowing NebulaStore to use S3 as a storage backend.

## Features

- **Object-based Storage**: Files are stored as objects in S3 buckets
- **Large File Support**: Files larger than S3 limits are automatically split across multiple objects
- **Blob Management**: Efficient handling of binary data using S3's object storage
- **Caching**: Optional caching layer for improved performance
- **Thread-Safe**: All operations are designed to be thread-safe
- **S3 Compliance**: Follows AWS S3 naming conventions and limits
- **Multi-Region Support**: Works with any AWS region or S3-compatible service

## Prerequisites

This module requires the AWS SDK for .NET:
- `AWSSDK.S3` (version 3.7.400.44 or later)

You must also have:
1. An AWS account with S3 access
2. Proper authentication configured (access keys, IAM roles, etc.)
3. An S3 bucket created for storage

## Usage

### Basic Usage

```csharp
using Amazon;
using Amazon.S3;
using NebulaStore.Afs.Aws.S3;
using NebulaStore.Afs.Blobstore;
using NebulaStore.Storage.Embedded;

// Create S3 client
var s3Client = new AmazonS3Client("access-key", "secret-key", RegionEndpoint.USEast1);

// Create AFS configuration with S3
var config = EmbeddedStorageConfiguration.New()
.SetStorageDirectory("s3-storage")
.SetUseAfs(true)
.SetAfsStorageType("s3")
.SetAfsConnectionString("bucket-name")
.Build();

using var storage = EmbeddedStorage.StartWithAfs(config);

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

### Direct AFS Usage

```csharp
using Amazon;
using Amazon.S3;
using NebulaStore.Afs.Aws.S3;
using NebulaStore.Afs.Blobstore;

// Create S3 client
var s3Client = new AmazonS3Client("access-key", "secret-key", RegionEndpoint.USEast1);

// Create S3 configuration
var s3Config = AwsS3Configuration.New()
.SetCredentials("access-key", "secret-key")
.SetRegion(RegionEndpoint.USEast1)
.SetUseCache(true);

// Create connector
using var connector = AwsS3Connector.New(s3Client, s3Config);

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

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

fileSystem.IoHandler.WriteData(path, new[] { data });
var readData = fileSystem.IoHandler.ReadData(path, 0, -1);
```

### Advanced Configuration

```csharp
using Amazon;
using Amazon.S3;
using NebulaStore.Afs.Aws.S3;

// Create advanced S3 configuration
var s3Config = AwsS3Configuration.New()
.SetCredentials("access-key", "secret-key")
.SetRegion(RegionEndpoint.USWest2)
.SetUseCache(true)
.SetTimeout(60000) // 60 seconds
.SetMaxRetryAttempts(5)
.SetForcePathStyle(false)
.SetUseHttps(true);

// For S3-compatible services (like MinIO)
var minioConfig = AwsS3Configuration.New()
.SetCredentials("minio-access-key", "minio-secret-key")
.SetServiceUrl("http://localhost:9000")
.SetForcePathStyle(true)
.SetUseHttps(false);

var s3Client = new AmazonS3Client(new AmazonS3Config
{
ServiceURL = minioConfig.ServiceUrl,
ForcePathStyle = minioConfig.ForcePathStyle,
UseHttp = !minioConfig.UseHttps
});

using var connector = AwsS3Connector.New(s3Client, minioConfig);
```

## Configuration Options

### S3-Specific Settings

- **AccessKeyId**: AWS access key ID
- **SecretAccessKey**: AWS secret access key
- **SessionToken**: AWS session token (for temporary credentials)
- **Region**: AWS region endpoint
- **ServiceUrl**: Custom service URL for S3-compatible services
- **ForcePathStyle**: Force path-style addressing (required for some S3-compatible services)
- **UseHttps**: Use HTTPS for connections (default: true)
- **UseCache**: Enable caching for improved performance (default: true)
- **TimeoutMilliseconds**: Timeout for S3 operations (default: 30000)
- **MaxRetryAttempts**: Maximum retry attempts for failed operations (default: 3)

### Bucket Naming Requirements

S3 bucket names must follow AWS naming conventions:
- Between 3 and 63 characters long
- Contain only lowercase letters, numbers, periods (.) and dashes (-)
- Begin with a lowercase letter or number
- Not end with a dash (-)
- Not contain consecutive periods (..)
- Not have dashes adjacent to periods (.- or -.)
- Not be formatted as an IP address
- Not start with 'xn--'

## Performance Considerations

### Caching

The S3 adapter includes an optional caching layer that can significantly improve performance:

```csharp
// Enable caching (recommended for production)
.SetUseCache(true)

// Disable caching (useful for testing or low-memory environments)
.SetUseCache(false)
```

### Large File Handling

Files are automatically split into multiple S3 objects when they exceed practical limits. The adapter handles this transparently, providing seamless read/write operations regardless of file size.

### Network Optimization

- Use appropriate timeout values for your network conditions
- Configure retry attempts based on your reliability requirements
- Consider using S3 Transfer Acceleration for global applications
- Use appropriate S3 storage classes for your access patterns

## Error Handling

S3 operations include comprehensive error handling:

```csharp
try
{
using var storage = EmbeddedStorage.StartWithAfs(config);
// ... operations
}
catch (AmazonS3Exception ex)
{
// Handle S3-specific errors
Console.WriteLine($"S3 Error: {ex.ErrorCode} - {ex.Message}");
}
catch (Exception ex)
{
// Handle general errors
Console.WriteLine($"Error: {ex.Message}");
}
```

## Security Considerations

- Use IAM roles instead of access keys when possible
- Implement least-privilege access policies
- Enable S3 bucket encryption
- Use VPC endpoints for private network access
- Monitor access with CloudTrail
- Regularly rotate access credentials

## Limitations

- Maximum object size: 5TB (S3 limit)
- Maximum number of objects per bucket: Unlimited
- Bucket names must be globally unique
- Some operations may have eventual consistency
- Cross-region data transfer costs may apply

## Troubleshooting

### Common Issues

1. **Access Denied**: Check IAM permissions and bucket policies
2. **Bucket Not Found**: Verify bucket name and region
3. **Network Timeouts**: Increase timeout values or check network connectivity
4. **Invalid Bucket Name**: Ensure bucket name follows AWS naming conventions

### Debug Logging

Enable AWS SDK logging for detailed troubleshooting:

```csharp
AWSConfigs.LoggingConfig.LogTo = LoggingOptions.Console;
AWSConfigs.LoggingConfig.LogResponses = ResponseLoggingOption.Always;
AWSConfigs.LoggingConfig.LogMetrics = true;
```
Loading
Loading