# Cloud Storage and Processing Guide

# Available NuGet Packages for Cloud Integration

# MongoDB Integration Package (Pre-built Solution)

The Video Fingerprinting SDK provides a ready-to-use NuGet package for MongoDB integration:

Package: VisioForge.DotNet.VideoFingerPrinting.MongoDB
Version: 2025.8.7
Purpose: Complete MongoDB integration with GridFS support for fingerprint storage

# Installation

# Package Manager Console
Install-Package VisioForge.DotNet.VideoFingerPrinting.MongoDB -Version 2025.8.7

# .NET CLI
dotnet add package VisioForge.DotNet.VideoFingerPrinting.MongoDB --version 2025.8.7

# PackageReference
<PackageReference Include="VisioForge.DotNet.VideoFingerPrinting.MongoDB" Version="2025.8.7" />

# Key Features

The MongoDB package provides the VideoFingerprintDB class with these capabilities:

  • MongoDB GridFS Integration: Efficient storage of large fingerprint data
  • Cloud & Local Support: Works with both MongoDB Atlas (cloud) and local MongoDB deployments
  • Full CRUD Operations: Complete fingerprint management (Create, Read, Update, Delete)
  • Flexible Connection Options: Multiple constructor overloads for different connection scenarios

# Basic Usage with MongoDB Atlas

using VisioForge.DotNet.VideoFingerPrinting.MongoDB;

// Connect to MongoDB Atlas (cloud deployment)
var connectionString = "mongodb+srv://username:password@cluster.mongodb.net/";
var db = new VideoFingerprintDB(connectionString, "fingerprint-database");

// Store a fingerprint
var fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync("video.mp4");
await db.AddFingerprintAsync("video-id-123", fingerprint);

// Retrieve a fingerprint
var retrieved = await db.GetFingerprintAsync("video-id-123");

// Search for similar fingerprints
var searchResults = await db.SearchAsync(fingerprint, threshold: 0.85);

// Delete a fingerprint
await db.DeleteFingerprintAsync("video-id-123");

# MongoDB Atlas Cloud Deployment

The MongoDB package is particularly well-suited for cloud deployments using MongoDB Atlas, MongoDB's fully-managed cloud database service:

# Setting Up MongoDB Atlas

  1. Create a MongoDB Atlas Account: Sign up at cloud.mongodb.com
  2. Create a Cluster: Choose your cloud provider (AWS, Azure, or Google Cloud)
  3. Configure Network Access: Add IP addresses or enable access from anywhere
  4. Create Database User: Set up authentication credentials
  5. Get Connection String: Copy the connection string from the cluster overview

# Advanced MongoDB Atlas Usage

using VisioForge.DotNet.VideoFingerPrinting.MongoDB;
using MongoDB.Driver;

public class CloudFingerprintService
{
    private readonly VideoFingerprintDB _db;
    
    public CloudFingerprintService(string atlasConnectionString)
    {
        // Connection string format for Atlas:
        // mongodb+srv://username:password@cluster.mongodb.net/?retryWrites=true&w=majority
        _db = new VideoFingerprintDB(atlasConnectionString, "production-fingerprints");
    }
    
    /// <summary>
    /// Process and store video fingerprint in MongoDB Atlas
    /// </summary>
    public async Task<string> ProcessAndStoreVideoAsync(string videoPath, string videoId)
    {
        // Generate fingerprint
        var fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync(videoPath);
        
        // Store in MongoDB Atlas with metadata
        await _db.AddFingerprintAsync(videoId, fingerprint, new Dictionary<string, object>
        {
            ["uploadDate"] = DateTime.UtcNow,
            ["videoPath"] = videoPath,
            ["duration"] = fingerprint.Duration.TotalSeconds,
            ["resolution"] = $"{fingerprint.Width}x{fingerprint.Height}"
        });
        
        return videoId;
    }
    
    /// <summary>
    /// Batch search for similar videos
    /// </summary>
    public async Task<List<SearchResult>> FindSimilarVideosAsync(string videoPath, double threshold = 0.8)
    {
        var searchFingerprint = await VFPAnalyzer.GetSearchFingerprintForVideoFileAsync(videoPath);
        var results = await _db.SearchAsync(searchFingerprint, threshold);
        
        return results.Select(r => new SearchResult
        {
            VideoId = r.Id,
            Similarity = r.Similarity,
            Metadata = r.Metadata
        }).ToList();
    }
}

// MongoDB Atlas with connection pooling and retry logic
public class ResilientMongoDBService
{
    private readonly VideoFingerprintDB _db;
    private readonly MongoClientSettings _settings;
    
    public ResilientMongoDBService(string atlasConnectionString)
    {
        // Configure connection with retry and pooling
        _settings = MongoClientSettings.FromConnectionString(atlasConnectionString);
        _settings.MaxConnectionPoolSize = 100;
        _settings.MinConnectionPoolSize = 10;
        _settings.ServerSelectionTimeout = TimeSpan.FromSeconds(30);
        _settings.RetryReads = true;
        _settings.RetryWrites = true;
        
        var client = new MongoClient(_settings);
        _db = new VideoFingerprintDB(client, "fingerprint-database");
    }
}

# MongoDB Atlas Features for Video Fingerprinting

  • Global Clusters: Deploy fingerprints across multiple regions for low latency
  • Auto-scaling: Automatically scale storage and compute based on load
  • Atlas Search: Full-text search capabilities for metadata
  • Change Streams: Real-time notifications when fingerprints are added/modified
  • Backup & Recovery: Automated backups with point-in-time recovery
  • Security: Encryption at rest and in transit, IP whitelisting, AWS PrivateLink

# Azure and AWS Integration (Implementation Patterns)

Important Note: Unlike the MongoDB package, Azure and AWS integrations are provided as implementation patterns and code examples that you need to adapt for your specific requirements. You'll need to install the respective cloud provider SDKs:

# Required Cloud Provider SDKs

For Azure integration:

# Azure Storage
Install-Package Azure.Storage.Blobs
# Azure Functions
Install-Package Microsoft.Azure.Functions.Worker
# Azure Identity (for managed identities)
Install-Package Azure.Identity

For AWS integration:

# AWS SDK for S3
Install-Package AWSSDK.S3
# AWS Lambda
Install-Package Amazon.Lambda.Core
Install-Package Amazon.Lambda.APIGatewayEvents
# AWS SQS (for queue processing)
Install-Package AWSSDK.SQS

# Core SDK APIs for Cloud Integration

The Video Fingerprinting SDK provides these essential classes for all cloud implementations:

  • VFPAnalyzer (static) - Generate fingerprints from video files:

    • GetComparingFingerprintForVideoFileAsync(filename, progressCallback)
    • GetSearchFingerprintForVideoFileAsync(filename, progressCallback, cancellationToken)
    • SetLicenseKey(licenseKey) - Set SDK license
  • VFPFingerPrint - Fingerprint data with serialization:

    • Save(Stream) - Save to any stream (memory, file, network)
    • Load(Stream) or Load(byte[]) - Load from stream or bytes
    • Perfect for cloud storage (S3, Azure Blob, MongoDB GridFS)
  • VFPFingerprintSource - Specifies video file source:

    • Filename property - Path to video file
    • StartTime, StopTime - Process specific segments
  • VFPCompare & VFPSearch - Static classes for operations:

    • VFPCompare.Process(), Build(), Compare()
    • VFPSearch.Process(), Build(), Search()

Cloud Integration Pattern:

  1. Download video from cloud storage to local temp file
  2. Process with VFPAnalyzer.GetComparingFingerprintForVideoFileAsync()
  3. Serialize fingerprint with Save() to memory stream
  4. Upload serialized data to cloud storage

Note: The SDK works with video FILES, not streams. For cloud videos, download to temp file first.

This comprehensive guide covers cloud-based video fingerprinting workflows, including pre-built MongoDB integration and implementation patterns for Azure and AWS, serverless processing, distributed architectures, and cost optimization strategies.

# Overview

Cloud-based video fingerprinting offers significant advantages for scalability, cost-effectiveness, and global distribution. This guide covers implementation patterns for major cloud providers and architectural best practices.

# Benefits of Cloud-Based Fingerprinting

  • Scalability: Auto-scale processing based on demand
  • Cost Efficiency: Pay only for resources used
  • Global Distribution: Process and store fingerprints near users
  • Managed Services: Leverage cloud-native services for storage, queuing, and processing
  • High Availability: Built-in redundancy and failover
  • Maintenance-Free: No infrastructure management overhead

# Azure Integration

# Azure Blob Storage for Fingerprint Storage

Azure Blob Storage provides scalable, cost-effective storage for video files and fingerprints.

# Setting Up Azure Storage

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using System;
using System.IO;
using System.Threading.Tasks;
using VisioForge.Core.VideoFingerPrinting;

public class AzureFingerprintStorage
{
    private readonly BlobServiceClient _blobServiceClient;
    private readonly string _containerName;
    
    public AzureFingerprintStorage(string connectionString, string containerName)
    {
        _blobServiceClient = new BlobServiceClient(connectionString);
        _containerName = containerName;
        
        // Ensure container exists
        var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
        containerClient.CreateIfNotExists(PublicAccessType.None);
    }
    
    /// <summary>
    /// Upload fingerprint to Azure Blob Storage
    /// </summary>
    public async Task<string> UploadFingerprintAsync(
        VFPFingerPrint fingerprint, 
        string videoId,
        Dictionary<string, string> metadata = null)
    {
        var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
        
        // Create hierarchical structure for organization
        string blobName = $"fingerprints/{DateTime.UtcNow:yyyy/MM/dd}/{videoId}.vfp";
        var blobClient = containerClient.GetBlobClient(blobName);
        
        // Serialize fingerprint to memory stream
        using var stream = new MemoryStream();
        fingerprint.Save(stream);
        stream.Position = 0;
        
        // Set metadata
        var blobMetadata = new Dictionary<string, string>
        {
            ["videoId"] = videoId,
            ["duration"] = fingerprint.Duration.ToString(),
            ["resolution"] = $"{fingerprint.Width}x{fingerprint.Height}",
            ["createdUtc"] = DateTime.UtcNow.ToString("O")
        };
        
        if (metadata != null)
        {
            foreach (var kvp in metadata)
            {
                blobMetadata[kvp.Key] = kvp.Value;
            }
        }
        
        // Upload with metadata
        var uploadOptions = new BlobUploadOptions
        {
            Metadata = blobMetadata,
            Tags = new Dictionary<string, string>
            {
                ["type"] = "fingerprint",
                ["version"] = "1.0"
            }
        };
        
        await blobClient.UploadAsync(stream, uploadOptions);
        
        return blobClient.Uri.ToString();
    }
    
    /// <summary>
    /// Download fingerprint from Azure Blob Storage
    /// </summary>
    public async Task<VFPFingerPrint> DownloadFingerprintAsync(string blobUri)
    {
        var blobClient = new BlobClient(new Uri(blobUri));
        
        // Download to memory
        var response = await blobClient.DownloadContentAsync();
        var bytes = response.Value.Content.ToArray();
        
        // Deserialize fingerprint
        using var stream = new MemoryStream(bytes);
        return VFPFingerPrint.Load(stream);
    }
    
    /// <summary>
    /// List fingerprints with optional filtering
    /// </summary>
    public async Task<List<FingerprintMetadata>> ListFingerprintsAsync(
        string prefix = null,
        DateTimeOffset? createdAfter = null)
    {
        var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
        var results = new List<FingerprintMetadata>();
        
        // Build query
        string query = "type = 'fingerprint'";
        
        await foreach (var blobItem in containerClient.GetBlobsAsync(
            prefix: prefix ?? "fingerprints/",
            traits: BlobTraits.Metadata | BlobTraits.Tags))
        {
            if (createdAfter.HasValue && 
                blobItem.Properties.CreatedOn < createdAfter.Value)
                continue;
            
            results.Add(new FingerprintMetadata
            {
                Uri = containerClient.GetBlobClient(blobItem.Name).Uri.ToString(),
                VideoId = blobItem.Metadata.GetValueOrDefault("videoId"),
                Duration = blobItem.Metadata.GetValueOrDefault("duration"),
                Resolution = blobItem.Metadata.GetValueOrDefault("resolution"),
                CreatedUtc = DateTimeOffset.Parse(
                    blobItem.Metadata.GetValueOrDefault("createdUtc")),
                Size = blobItem.Properties.ContentLength ?? 0
            });
        }
        
        return results;
    }
}

public class FingerprintMetadata
{
    public string Uri { get; set; }
    public string VideoId { get; set; }
    public string Duration { get; set; }
    public string Resolution { get; set; }
    public DateTimeOffset CreatedUtc { get; set; }
    public long Size { get; set; }
}

# Implementing Tiered Storage

using Azure.Storage.Blobs.Models;

public class TieredFingerprintStorage : AzureFingerprintStorage
{
    /// <summary>
    /// Move older fingerprints to cool/archive tier for cost savings
    /// </summary>
    public async Task OptimizeStorageTiersAsync(int hotTierDays = 7, int coolTierDays = 30)
    {
        var containerClient = _blobServiceClient.GetBlobContainerClient(_containerName);
        
        await foreach (var blobItem in containerClient.GetBlobsAsync(
            traits: BlobTraits.Metadata))
        {
            var age = DateTime.UtcNow - blobItem.Properties.CreatedOn.Value.DateTime;
            var blobClient = containerClient.GetBlobClient(blobItem.Name);
            
            AccessTier targetTier;
            
            if (age.TotalDays <= hotTierDays)
            {
                targetTier = AccessTier.Hot;
            }
            else if (age.TotalDays <= coolTierDays)
            {
                targetTier = AccessTier.Cool;
            }
            else
            {
                targetTier = AccessTier.Archive;
            }
            
            // Only change if different from current tier
            if (blobItem.Properties.AccessTier != targetTier.ToString())
            {
                await blobClient.SetAccessTierAsync(targetTier);
                
                Console.WriteLine($"Moved {blobItem.Name} to {targetTier} tier");
            }
        }
    }
    
    /// <summary>
    /// Rehydrate archived fingerprint for access
    /// </summary>
    public async Task<bool> RehydrateFingerprintAsync(string blobUri, AccessTier targetTier = AccessTier.Hot)
    {
        var blobClient = new BlobClient(new Uri(blobUri));
        
        var properties = await blobClient.GetPropertiesAsync();
        
        if (properties.Value.AccessTier == "Archive")
        {
            await blobClient.SetAccessTierAsync(targetTier, rehydratePriority: RehydratePriority.High);
            
            // Wait for rehydration (this could take hours for standard priority)
            while (properties.Value.ArchiveStatus == "rehydrate-pending-to-hot")
            {
                await Task.Delay(TimeSpan.FromMinutes(1));
                properties = await blobClient.GetPropertiesAsync();
            }
            
            return true;
        }
        
        return false;
    }
}

# Azure Functions for Serverless Processing

Azure Functions provide serverless compute for processing videos on-demand.

# Function App Setup

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using VisioForge.Core.VideoFingerPrinting;
using VisioForge.Core.Types.X.Sources;

public class FingerprintFunction
{
    private readonly ILogger<FingerprintFunction> _logger;
    private readonly AzureFingerprintStorage _storage;
    
    public FingerprintFunction(ILogger<FingerprintFunction> logger)
    {
        _logger = logger;
        _storage = new AzureFingerprintStorage(
            Environment.GetEnvironmentVariable("AzureStorageConnection"),
            "fingerprints"
        );
        
        // Initialize SDK
        VFPAnalyzer.SetLicenseKey(Environment.GetEnvironmentVariable("VFP_LICENSE_KEY"));
    }
    
    /// <summary>
    /// HTTP-triggered function for on-demand fingerprint generation
    /// </summary>
    [Function("GenerateFingerprint")]
    public async Task<HttpResponseData> GenerateFingerprint(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
    {
        _logger.LogInformation("Processing fingerprint generation request");
        
        // Parse request
        var requestBody = await req.ReadAsStringAsync();
        var request = JsonSerializer.Deserialize<FingerprintRequest>(requestBody);
        
        try
        {
            // Download video from blob storage
            var videoPath = await DownloadVideoAsync(request.VideoUri);
            
            // Configure fingerprint generation
            var source = new VFPFingerprintSource(videoPath)
            {
                CustomResolution = new System.Drawing.Size(640, 480),
                FrameRate = request.FrameRate ?? 10
            };
            
            if (request.StartTime.HasValue)
                source.StartTime = TimeSpan.FromSeconds(request.StartTime.Value);
            
            if (request.StopTime.HasValue)
                source.StopTime = TimeSpan.FromSeconds(request.StopTime.Value);
            
            // Generate fingerprint
            var fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync(
                source,
                errorDelegate: (error) => _logger.LogError($"Fingerprint error: {error}")
            );
            
            if (fingerprint != null)
            {
                // Upload to storage
                var fingerprintUri = await _storage.UploadFingerprintAsync(
                    fingerprint, 
                    request.VideoId,
                    new Dictionary<string, string>
                    {
                        ["sourceVideo"] = request.VideoUri,
                        ["processedBy"] = Environment.MachineName
                    }
                );
                
                // Return success response
                var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
                await response.WriteAsJsonAsync(new FingerprintResponse
                {
                    Success = true,
                    FingerprintUri = fingerprintUri,
                    Duration = fingerprint.Duration.TotalSeconds,
                    ProcessingTime = DateTime.UtcNow.Subtract(request.RequestTime).TotalSeconds
                });
                
                return response;
            }
            
            throw new Exception("Failed to generate fingerprint");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error generating fingerprint");
            
            var response = req.CreateResponse(System.Net.HttpStatusCode.InternalServerError);
            await response.WriteAsJsonAsync(new FingerprintResponse
            {
                Success = false,
                Error = ex.Message
            });
            
            return response;
        }
        finally
        {
            // Cleanup temporary files
            CleanupTempFiles();
        }
    }
    
    /// <summary>
    /// Queue-triggered function for batch processing
    /// </summary>
    [Function("ProcessFingerprintQueue")]
    public async Task ProcessQueue(
        [QueueTrigger("fingerprint-requests")] FingerprintRequest request)
    {
        _logger.LogInformation($"Processing queued request for video: {request.VideoId}");
        
        try
        {
            // Similar processing logic as HTTP trigger
            // but optimized for background processing
            
            // Download video
            var videoPath = await DownloadVideoAsync(request.VideoUri);
            
            // Generate fingerprint with retry logic
            VFPFingerPrint fingerprint = null;
            int maxRetries = 3;
            
            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    var source = new VFPFingerprintSource(videoPath)
                    {
                        CustomResolution = new System.Drawing.Size(640, 480)
                    };
                    
                    fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync(source);
                    
                    if (fingerprint != null)
                        break;
                }
                catch (Exception ex)
                {
                    _logger.LogWarning($"Attempt {i + 1} failed: {ex.Message}");
                    
                    if (i == maxRetries - 1)
                        throw;
                    
                    await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, i))); // Exponential backoff
                }
            }
            
            // Store fingerprint
            if (fingerprint != null)
            {
                await _storage.UploadFingerprintAsync(fingerprint, request.VideoId);
                
                // Send completion notification
                await NotifyCompletionAsync(request.VideoId, true);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Failed to process video {request.VideoId}");
            await NotifyCompletionAsync(request.VideoId, false, ex.Message);
            throw; // Re-throw to trigger retry policies
        }
    }
    
    private async Task<string> DownloadVideoAsync(string videoUri)
    {
        // Implementation to download video from blob storage
        // Returns local path to downloaded file
        var tempPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.mp4");
        
        using var httpClient = new HttpClient();
        using var response = await httpClient.GetAsync(videoUri);
        using var fileStream = File.Create(tempPath);
        await response.Content.CopyToAsync(fileStream);
        
        return tempPath;
    }
    
    private void CleanupTempFiles()
    {
        // Clean up temporary video files
        var tempDir = Path.GetTempPath();
        var files = Directory.GetFiles(tempDir, "*.mp4")
            .Where(f => File.GetCreationTime(f) < DateTime.Now.AddHours(-1));
        
        foreach (var file in files)
        {
            try { File.Delete(file); } catch { }
        }
    }
    
    private async Task NotifyCompletionAsync(string videoId, bool success, string error = null)
    {
        // Send notification via Service Bus, Event Grid, or webhook
        // Implementation depends on your notification strategy
    }
}

public class FingerprintRequest
{
    public string VideoId { get; set; }
    public string VideoUri { get; set; }
    public int? FrameRate { get; set; }
    public double? StartTime { get; set; }
    public double? StopTime { get; set; }
    public DateTime RequestTime { get; set; } = DateTime.UtcNow;
}

public class FingerprintResponse
{
    public bool Success { get; set; }
    public string FingerprintUri { get; set; }
    public double Duration { get; set; }
    public double ProcessingTime { get; set; }
    public string Error { get; set; }
}

# Durable Functions for Complex Workflows

using Microsoft.Azure.Functions.Worker;
using Microsoft.DurableTask;
using Microsoft.DurableTask.Client;

public class DurableFingerprintOrchestrator
{
    /// <summary>
    /// Orchestrator function for complex fingerprinting workflows
    /// </summary>
    [Function("FingerprintOrchestrator")]
    public static async Task<FingerprintWorkflowResult> RunOrchestrator(
        [OrchestrationTrigger] TaskOrchestrationContext context)
    {
        var input = context.GetInput<FingerprintWorkflowInput>();
        var results = new FingerprintWorkflowResult();
        
        try
        {
            // Step 1: Validate video
            var validation = await context.CallActivityAsync<ValidationResult>(
                "ValidateVideo", 
                input.VideoUri);
            
            if (!validation.IsValid)
            {
                results.Success = false;
                results.Error = validation.Error;
                return results;
            }
            
            // Step 2: Split video into segments for parallel processing
            var segments = await context.CallActivityAsync<List<VideoSegment>>(
                "SplitVideo",
                new SplitVideoInput
                {
                    VideoUri = input.VideoUri,
                    SegmentDuration = 300 // 5 minutes
                });
            
            // Step 3: Process segments in parallel
            var fingerprintTasks = segments.Select(segment =>
                context.CallActivityAsync<SegmentFingerprint>(
                    "GenerateSegmentFingerprint",
                    segment)
            ).ToList();
            
            var segmentFingerprints = await Task.WhenAll(fingerprintTasks);
            
            // Step 4: Merge fingerprints
            var mergedFingerprint = await context.CallActivityAsync<string>(
                "MergeFingerprints",
                segmentFingerprints);
            
            // Step 5: Store and index
            var storageResult = await context.CallActivityAsync<StorageResult>(
                "StoreAndIndexFingerprint",
                new StoreInput
                {
                    FingerprintUri = mergedFingerprint,
                    VideoId = input.VideoId,
                    Metadata = input.Metadata
                });
            
            results.Success = true;
            results.FingerprintUri = storageResult.Uri;
            results.ProcessingTime = context.CurrentUtcDateTime.Subtract(
                context.GetInput<FingerprintWorkflowInput>().StartTime).TotalSeconds;
        }
        catch (Exception ex)
        {
            results.Success = false;
            results.Error = ex.Message;
        }
        
        return results;
    }
    
    /// <summary>
    /// HTTP trigger to start orchestration
    /// </summary>
    [Function("StartFingerprintWorkflow")]
    public static async Task<HttpResponseData> StartWorkflow(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req,
        [DurableClient] DurableTaskClient client)
    {
        var input = await req.ReadFromJsonAsync<FingerprintWorkflowInput>();
        input.StartTime = DateTime.UtcNow;
        
        // Start orchestration
        string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
            "FingerprintOrchestrator",
            input);
        
        // Return status check URLs
        var response = req.CreateResponse(System.Net.HttpStatusCode.Accepted);
        response.Headers.Add("Location", $"/api/status/{instanceId}");
        
        await response.WriteAsJsonAsync(new
        {
            instanceId,
            statusQueryGetUri = $"/api/status/{instanceId}",
            message = "Fingerprint workflow started"
        });
        
        return response;
    }
}

public class FingerprintWorkflowInput
{
    public string VideoId { get; set; }
    public string VideoUri { get; set; }
    public Dictionary<string, string> Metadata { get; set; }
    public DateTime StartTime { get; set; }
}

public class FingerprintWorkflowResult
{
    public bool Success { get; set; }
    public string FingerprintUri { get; set; }
    public double ProcessingTime { get; set; }
    public string Error { get; set; }
}

# AWS Integration

# AWS S3 Fingerprint Storage Patterns

Amazon S3 provides highly durable object storage for fingerprints and videos.

# S3 Storage Implementation

using Amazon.S3;
using Amazon.S3.Model;
using System.Text.Json;

public class S3FingerprintStorage
{
    private readonly IAmazonS3 _s3Client;
    private readonly string _bucketName;
    
    public S3FingerprintStorage(string region, string bucketName)
    {
        _s3Client = new AmazonS3Client(Amazon.RegionEndpoint.GetBySystemName(region));
        _bucketName = bucketName;
        
        // Ensure bucket exists
        EnsureBucketExistsAsync().Wait();
    }
    
    private async Task EnsureBucketExistsAsync()
    {
        try
        {
            await _s3Client.HeadBucketAsync(new HeadBucketRequest { BucketName = _bucketName });
        }
        catch (AmazonS3Exception ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
        {
            await _s3Client.PutBucketAsync(new PutBucketRequest
            {
                BucketName = _bucketName,
                BucketRegion = S3Region.USEast1
            });
            
            // Enable versioning for data protection
            await _s3Client.PutBucketVersioningAsync(new PutBucketVersioningRequest
            {
                BucketName = _bucketName,
                VersioningConfig = new S3BucketVersioningConfig
                {
                    Status = VersionStatus.Enabled
                }
            });
        }
    }
    
    /// <summary>
    /// Upload fingerprint with S3 intelligent tiering
    /// </summary>
    public async Task<string> UploadFingerprintAsync(
        VFPFingerPrint fingerprint,
        string videoId,
        Dictionary<string, string> metadata = null)
    {
        // Create S3 key with date partitioning for better organization
        string s3Key = $"fingerprints/{DateTime.UtcNow:yyyy/MM/dd}/{videoId}.vfp";
        
        // Serialize fingerprint
        using var stream = new MemoryStream();
        fingerprint.Save(stream);
        stream.Position = 0;
        
        var putRequest = new PutObjectRequest
        {
            BucketName = _bucketName,
            Key = s3Key,
            InputStream = stream,
            ContentType = "application/octet-stream",
            StorageClass = S3StorageClass.IntelligentTiering, // Automatic cost optimization
            ServerSideEncryptionMethod = ServerSideEncryptionMethod.AES256,
            Metadata = 
            {
                ["video-id"] = videoId,
                ["duration"] = fingerprint.Duration.TotalSeconds.ToString(),
                ["resolution"] = $"{fingerprint.Width}x{fingerprint.Height}",
                ["created-utc"] = DateTime.UtcNow.ToString("O")
            }
        };
        
        // Add custom metadata
        if (metadata != null)
        {
            foreach (var kvp in metadata)
            {
                putRequest.Metadata[kvp.Key] = kvp.Value;
            }
        }
        
        // Add tags for lifecycle management
        putRequest.TagSet = new List<Tag>
        {
            new Tag { Key = "Type", Value = "Fingerprint" },
            new Tag { Key = "Version", Value = "1.0" },
            new Tag { Key = "Environment", Value = Environment.GetEnvironmentVariable("ENVIRONMENT") ?? "Production" }
        };
        
        var response = await _s3Client.PutObjectAsync(putRequest);
        
        // Return S3 URI
        return $"s3://{_bucketName}/{s3Key}";
    }
    
    /// <summary>
    /// Download fingerprint with caching support
    /// </summary>
    public async Task<VFPFingerPrint> DownloadFingerprintAsync(string s3Uri, bool useCache = true)
    {
        // Parse S3 URI
        var uri = new Uri(s3Uri);
        string key = uri.AbsolutePath.TrimStart('/');
        
        // Check local cache first
        if (useCache)
        {
            var cached = GetFromCache(key);
            if (cached != null)
                return cached;
        }
        
        var getRequest = new GetObjectRequest
        {
            BucketName = _bucketName,
            Key = key
        };
        
        using var response = await _s3Client.GetObjectAsync(getRequest);
        using var memoryStream = new MemoryStream();
        await response.ResponseStream.CopyToAsync(memoryStream);
        
        memoryStream.Position = 0;
        var fingerprint = VFPFingerPrint.Load(memoryStream);
        
        // Add to cache
        if (useCache)
        {
            AddToCache(key, fingerprint);
        }
        
        return fingerprint;
    }
    
    /// <summary>
    /// Batch upload fingerprints with S3 Transfer Utility
    /// </summary>
    public async Task<List<string>> BatchUploadFingerprintsAsync(
        Dictionary<string, VFPFingerPrint> fingerprints)
    {
        var uploadTasks = new List<Task<string>>();
        
        // Use S3 Transfer Utility for optimized uploads
        using var transferUtility = new Amazon.S3.Transfer.TransferUtility(_s3Client);
        
        foreach (var kvp in fingerprints)
        {
            uploadTasks.Add(Task.Run(async () =>
            {
                using var stream = new MemoryStream();
                kvp.Value.Save(stream);
                stream.Position = 0;
                
                var uploadRequest = new Amazon.S3.Transfer.TransferUtilityUploadRequest
                {
                    BucketName = _bucketName,
                    Key = $"fingerprints/batch/{DateTime.UtcNow:yyyy-MM-dd}/{kvp.Key}.vfp",
                    InputStream = stream,
                    StorageClass = S3StorageClass.IntelligentTiering,
                    PartSize = 5 * 1024 * 1024 // 5MB parts for multipart upload
                };
                
                await transferUtility.UploadAsync(uploadRequest);
                
                return $"s3://{_bucketName}/{uploadRequest.Key}";
            }));
        }
        
        return (await Task.WhenAll(uploadTasks)).ToList();
    }
    
    /// <summary>
    /// Query fingerprints using S3 Select
    /// </summary>
    public async Task<List<FingerprintQueryResult>> QueryFingerprintsAsync(
        string sqlExpression)
    {
        // First, create an inventory of fingerprints in JSON format
        var listRequest = new ListObjectsV2Request
        {
            BucketName = _bucketName,
            Prefix = "fingerprints/"
        };
        
        var objects = new List<S3Object>();
        ListObjectsV2Response listResponse;
        
        do
        {
            listResponse = await _s3Client.ListObjectsV2Async(listRequest);
            objects.AddRange(listResponse.S3Objects);
            listRequest.ContinuationToken = listResponse.NextContinuationToken;
        } while (listResponse.IsTruncated);
        
        // Use S3 Select for complex queries
        var selectRequest = new SelectObjectContentRequest
        {
            BucketName = _bucketName,
            Key = "fingerprints/inventory.json", // Assuming we maintain an inventory
            Expression = sqlExpression,
            ExpressionType = ExpressionType.SQL,
            InputSerialization = new InputSerialization
            {
                JSON = new JSONInput
                {
                    JsonType = JsonType.Lines
                }
            },
            OutputSerialization = new OutputSerialization
            {
                JSON = new JSONOutput()
            }
        };
        
        var results = new List<FingerprintQueryResult>();
        
        try
        {
            using var selectResponse = await _s3Client.SelectObjectContentAsync(selectRequest);
            
            foreach (var ev in selectResponse.Payload)
            {
                if (ev is RecordsEvent records)
                {
                    using var reader = new StreamReader(records.Payload);
                    var json = await reader.ReadToEndAsync();
                    var result = JsonSerializer.Deserialize<FingerprintQueryResult>(json);
                    results.Add(result);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"S3 Select query failed: {ex.Message}");
        }
        
        return results;
    }
    
    private VFPFingerPrint GetFromCache(string key)
    {
        // Implement local caching logic
        // Could use MemoryCache, Redis, or file-based cache
        return null;
    }
    
    private void AddToCache(string key, VFPFingerPrint fingerprint)
    {
        // Add to cache implementation
    }
}

public class FingerprintQueryResult
{
    public string VideoId { get; set; }
    public string S3Uri { get; set; }
    public double Duration { get; set; }
    public DateTime CreatedUtc { get; set; }
}

# AWS Lambda Implementation

AWS Lambda provides serverless compute for processing videos at scale.

# Lambda Function Setup

using Amazon.Lambda.Core;
using Amazon.Lambda.SQSEvents;
using Amazon.Lambda.APIGatewayEvents;
using System.Text.Json;

// Assembly attribute to enable Lambda function
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

public class FingerprintLambdaFunction
{
    private readonly S3FingerprintStorage _storage;
    
    public FingerprintLambdaFunction()
    {
        // Initialize in constructor for Lambda container reuse
        _storage = new S3FingerprintStorage(
            Environment.GetEnvironmentVariable("AWS_REGION"),
            Environment.GetEnvironmentVariable("S3_BUCKET")
        );
        
        VFPAnalyzer.SetLicenseKey(Environment.GetEnvironmentVariable("VFP_LICENSE_KEY"));
    }
    
    /// <summary>
    /// API Gateway triggered Lambda for synchronous processing
    /// </summary>
    public async Task<APIGatewayProxyResponse> HandleApiRequest(
        APIGatewayProxyRequest request,
        ILambdaContext context)
    {
        context.Logger.LogLine($"Processing API request: {request.RequestContext.RequestId}");
        
        try
        {
            var fingerprintRequest = JsonSerializer.Deserialize<FingerprintRequest>(request.Body);
            
            // Process with timeout awareness
            var cts = new CancellationTokenSource();
            cts.CancelAfter(TimeSpan.FromMilliseconds(context.RemainingTime.TotalMilliseconds - 5000)); // 5 sec buffer
            
            var result = await ProcessFingerprintAsync(fingerprintRequest, cts.Token);
            
            return new APIGatewayProxyResponse
            {
                StatusCode = 200,
                Headers = new Dictionary<string, string> { ["Content-Type"] = "application/json" },
                Body = JsonSerializer.Serialize(result)
            };
        }
        catch (OperationCanceledException)
        {
            // Lambda is about to timeout, return partial result
            return new APIGatewayProxyResponse
            {
                StatusCode = 202,
                Body = JsonSerializer.Serialize(new { status = "Processing", message = "Request queued for async processing" })
            };
        }
        catch (Exception ex)
        {
            context.Logger.LogLine($"Error: {ex.Message}");
            
            return new APIGatewayProxyResponse
            {
                StatusCode = 500,
                Body = JsonSerializer.Serialize(new { error = ex.Message })
            };
        }
    }
    
    /// <summary>
    /// SQS triggered Lambda for asynchronous batch processing
    /// </summary>
    public async Task HandleSQSBatch(SQSEvent sqsEvent, ILambdaContext context)
    {
        var tasks = new List<Task>();
        
        foreach (var record in sqsEvent.Records)
        {
            tasks.Add(ProcessSQSMessageAsync(record, context));
        }
        
        await Task.WhenAll(tasks);
    }
    
    private async Task ProcessSQSMessageAsync(SQSEvent.SQSMessage message, ILambdaContext context)
    {
        try
        {
            var request = JsonSerializer.Deserialize<FingerprintRequest>(message.Body);
            
            context.Logger.LogLine($"Processing video: {request.VideoId}");
            
            // Download video from S3
            var videoPath = await DownloadFromS3Async(request.VideoUri);
            
            // VFPAnalyzer methods take file path and optional callbacks
            var fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync(
                videoPath,
                null); // progress callback
            
            if (fingerprint != null)
            {
                // Upload to S3
                var fingerprintUri = await _storage.UploadFingerprintAsync(
                    fingerprint,
                    request.VideoId,
                    new Dictionary<string, string>
                    {
                        ["processed-by"] = context.FunctionName,
                        ["request-id"] = context.RequestId
                    }
                );
                
                // Send success notification
                await SendNotificationAsync(request.VideoId, true, fingerprintUri);
            }
        }
        catch (Exception ex)
        {
            context.Logger.LogLine($"Error processing message: {ex.Message}");
            
            // Check if we should retry
            var receiveCount = int.Parse(message.Attributes.GetValueOrDefault("ApproximateReceiveCount", "1"));
            
            if (receiveCount >= 3)
            {
                // Move to DLQ after 3 attempts
                await SendToDLQAsync(message, ex.Message);
            }
            else
            {
                throw; // Re-throw to trigger retry
            }
        }
    }
    
    private async Task<FingerprintResult> ProcessFingerprintAsync(
        FingerprintRequest request,
        CancellationToken cancellationToken)
    {
        // Implementation of fingerprint processing
        // Similar to previous examples but with Lambda-specific optimizations
        
        return new FingerprintResult
        {
            Success = true,
            VideoId = request.VideoId,
            FingerprintUri = "s3://bucket/path/to/fingerprint.vfp"
        };
    }
    
    private async Task<string> DownloadFromS3Async(string s3Uri)
    {
        // Download video to Lambda's /tmp directory
        var tempPath = $"/tmp/{Guid.NewGuid()}.mp4";
        
        // Implementation to download from S3
        
        return tempPath;
    }
    
    private async Task SendNotificationAsync(string videoId, bool success, string fingerprintUri)
    {
        // Send SNS notification or update DynamoDB
    }
    
    private async Task SendToDLQAsync(SQSEvent.SQSMessage message, string error)
    {
        // Send to dead letter queue for manual review
    }
}

public class FingerprintResult
{
    public bool Success { get; set; }
    public string VideoId { get; set; }
    public string FingerprintUri { get; set; }
    public double ProcessingTime { get; set; }
}

# Step Functions for Complex Workflows

// State machine definition for AWS Step Functions
public class StepFunctionsWorkflow
{
    public static string GetStateMachineDefinition()
    {
        return @"
        {
          ""Comment"": ""Video fingerprinting workflow with parallel processing"",
          ""StartAt"": ""ValidateInput"",
          ""States"": {
            ""ValidateInput"": {
              ""Type"": ""Task"",
              ""Resource"": ""arn:aws:lambda:us-east-1:123456789:function:ValidateVideo"",
              ""Next"": ""CheckVideoSize"",
              ""Catch"": [{
                ""ErrorEquals"": [""ValidationError""],
                ""Next"": ""ValidationFailed""
              }]
            },
            ""CheckVideoSize"": {
              ""Type"": ""Choice"",
              ""Choices"": [{
                ""Variable"": ""$.videoSize"",
                ""NumericGreaterThan"": 1073741824,
                ""Next"": ""SplitVideo""
              }],
              ""Default"": ""ProcessSingleVideo""
            },
            ""SplitVideo"": {
              ""Type"": ""Task"",
              ""Resource"": ""arn:aws:lambda:us-east-1:123456789:function:SplitVideo"",
              ""Next"": ""ProcessSegments""
            },
            ""ProcessSegments"": {
              ""Type"": ""Map"",
              ""ItemsPath"": ""$.segments"",
              ""MaxConcurrency"": 10,
              ""Iterator"": {
                ""StartAt"": ""GenerateSegmentFingerprint"",
                ""States"": {
                  ""GenerateSegmentFingerprint"": {
                    ""Type"": ""Task"",
                    ""Resource"": ""arn:aws:lambda:us-east-1:123456789:function:GenerateFingerprint"",
                    ""End"": true
                  }
                }
              },
              ""Next"": ""MergeFingerprints""
            },
            ""MergeFingerprints"": {
              ""Type"": ""Task"",
              ""Resource"": ""arn:aws:lambda:us-east-1:123456789:function:MergeFingerprints"",
              ""Next"": ""StoreResult""
            },
            ""ProcessSingleVideo"": {
              ""Type"": ""Task"",
              ""Resource"": ""arn:aws:lambda:us-east-1:123456789:function:GenerateFingerprint"",
              ""Next"": ""StoreResult""
            },
            ""StoreResult"": {
              ""Type"": ""Task"",
              ""Resource"": ""arn:aws:lambda:us-east-1:123456789:function:StoreFingerprint"",
              ""Next"": ""SendNotification""
            },
            ""SendNotification"": {
              ""Type"": ""Task"",
              ""Resource"": ""arn:aws:sns:us-east-1:123456789:FingerprintComplete"",
              ""End"": true
            },
            ""ValidationFailed"": {
              ""Type"": ""Fail"",
              ""Error"": ""ValidationError"",
              ""Cause"": ""Input validation failed""
            }
          }
        }";
    }
}

# Distributed Processing Patterns

# Queue-Based Architecture

public class DistributedFingerprintProcessor
{
    private readonly IMessageQueue _queue;
    private readonly IDistributedCache _cache;
    private readonly VFPFingerPrintDB _localDb; // Local fingerprint database
    // Note: SDK provides VFPFingerPrintDB for local storage
    
    /// <summary>
    /// Producer - adds videos to processing queue
    /// </summary>
    public async Task QueueVideoForProcessingAsync(string videoUri, ProcessingPriority priority = ProcessingPriority.Normal)
    {
        var message = new ProcessingMessage
        {
            Id = Guid.NewGuid().ToString(),
            VideoUri = videoUri,
            Priority = priority,
            QueuedAt = DateTime.UtcNow,
            RetryCount = 0
        };
        
        // Add to appropriate queue based on priority
        string queueName = priority switch
        {
            ProcessingPriority.High => "fingerprint-high-priority",
            ProcessingPriority.Low => "fingerprint-low-priority",
            _ => "fingerprint-normal-priority"
        };
        
        await _queue.SendMessageAsync(queueName, message);
        
        // Track in distributed cache
        await _cache.SetAsync($"processing:{message.Id}", "queued", TimeSpan.FromHours(24));
    }
    
    /// <summary>
    /// Consumer - processes videos from queue
    /// </summary>
    public async Task ProcessQueueAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            try
            {
                // Get message from queue
                var message = await _queue.ReceiveMessageAsync<ProcessingMessage>("fingerprint-normal-priority");
                
                if (message == null)
                {
                    await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
                    continue;
                }
                
                // Update status
                await _cache.SetAsync($"processing:{message.Content.Id}", "processing");
                
                // Process fingerprint
                var result = await ProcessVideoAsync(message.Content);
                
                if (result.Success)
                {
                    // Mark as complete
                    await _queue.DeleteMessageAsync(message);
                    await _cache.SetAsync($"processing:{message.Content.Id}", "completed");
                }
                else
                {
                    // Handle failure
                    await HandleFailureAsync(message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Processing error: {ex.Message}");
            }
        }
    }
    
    private async Task<ProcessingResult> ProcessVideoAsync(ProcessingMessage message)
    {
        try
        {
            // Download video
            var videoPath = await DownloadVideoAsync(message.VideoUri);
            
            // Generate fingerprint with resource limits
            using var semaphore = new SemaphoreSlim(1, 1); // Limit concurrent processing
            await semaphore.WaitAsync();
            
            try
            {
                var fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync(
                    videoPath,
                    null); // progress callback
                
                if (fingerprint != null)
                {
                    // Store fingerprint
                    var uri = await _storage.UploadAsync(fingerprint, message.Id);
                    
                    return new ProcessingResult
                    {
                        Success = true,
                        FingerprintUri = uri
                    };
                }
            }
            finally
            {
                semaphore.Release();
            }
        }
        catch (Exception ex)
        {
            return new ProcessingResult
            {
                Success = false,
                Error = ex.Message
            };
        }
        
        return new ProcessingResult { Success = false };
    }
}

public enum ProcessingPriority
{
    Low,
    Normal,
    High
}

public class ProcessingMessage
{
    public string Id { get; set; }
    public string VideoUri { get; set; }
    public ProcessingPriority Priority { get; set; }
    public DateTime QueuedAt { get; set; }
    public int RetryCount { get; set; }
}

# Container-Based Processing with Kubernetes

# Kubernetes deployment for fingerprint processing
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fingerprint-processor
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fingerprint-processor
  template:
    metadata:
      labels:
        app: fingerprint-processor
    spec:
      containers:
      - name: processor
        image: myregistry/fingerprint-processor:latest
        resources:
          requests:
            memory: "2Gi"
            cpu: "1"
          limits:
            memory: "4Gi"
            cpu: "2"
        env:
        - name: VFP_LICENSE_KEY
          valueFrom:
            secretKeyRef:
              name: vfp-secrets
              key: license-key
        - name: STORAGE_CONNECTION
          valueFrom:
            secretKeyRef:
              name: storage-secrets
              key: connection-string
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: fingerprint-processor-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: fingerprint-processor
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

# Cost Optimization Strategies

# 1. Storage Optimization

public class CostOptimizedStorage
{
    /// <summary>
    /// Compress fingerprints before storage
    /// </summary>
    public async Task<byte[]> CompressFingerprintAsync(VFPFingerPrint fingerprint)
    {
        using var originalStream = new MemoryStream();
        fingerprint.Save(originalStream);
        
        using var compressedStream = new MemoryStream();
        using (var gzipStream = new System.IO.Compression.GZipStream(
            compressedStream, 
            System.IO.Compression.CompressionLevel.Optimal))
        {
            await originalStream.CopyToAsync(gzipStream);
        }
        
        var compressed = compressedStream.ToArray();
        
        // Log compression ratio
        double ratio = (double)compressed.Length / originalStream.Length;
        Console.WriteLine($"Compression ratio: {ratio:P2}");
        
        return compressed;
    }
    
    /// <summary>
    /// Implement intelligent caching
    /// </summary>
    public class TieredCache
    {
        private readonly MemoryCache _l1Cache; // Hot cache
        private readonly IDistributedCache _l2Cache; // Warm cache (Redis)
        private readonly VFPFingerPrintDB _coldStorage; // Cold storage database
        
        public async Task<VFPFingerPrint> GetFingerprintAsync(string id)
        {
            // Check L1 cache
            if (_l1Cache.TryGetValue(id, out VFPFingerPrint fingerprint))
            {
                return fingerprint;
            }
            
            // Check L2 cache
            var cached = await _l2Cache.GetAsync(id);
            if (cached != null)
            {
                fingerprint = DeserializeFingerprint(cached);
                _l1Cache.Set(id, fingerprint, TimeSpan.FromMinutes(5));
                return fingerprint;
            }
            
            // Fetch from cold storage
            fingerprint = await _l3Storage.DownloadAsync(id);
            
            // Populate caches
            await _l2Cache.SetAsync(id, SerializeFingerprint(fingerprint), TimeSpan.FromHours(1));
            _l1Cache.Set(id, fingerprint, TimeSpan.FromMinutes(5));
            
            return fingerprint;
        }
    }
}

# 2. Processing Optimization

public class ProcessingCostOptimizer
{
    /// <summary>
    /// Use spot/preemptible instances for batch processing
    /// </summary>
    public async Task<bool> ProcessWithSpotInstancesAsync(List<string> videoUris)
    {
        // Check spot instance availability and pricing
        var spotPrice = await GetCurrentSpotPriceAsync();
        var onDemandPrice = await GetOnDemandPriceAsync();
        
        if (spotPrice < onDemandPrice * 0.5) // 50% savings threshold
        {
            // Launch spot instances
            await LaunchSpotInstancesAsync(videoUris.Count / 10); // 10 videos per instance
            
            // Process with interruption handling
            return await ProcessWithInterruptionHandlingAsync(videoUris);
        }
        
        // Fall back to on-demand or serverless
        return await ProcessWithServerlessAsync(videoUris);
    }
    
    /// <summary>
    /// Batch processing for efficiency
    /// </summary>
    public async Task BatchProcessAsync(List<string> videoUris, int batchSize = 50)
    {
        var batches = videoUris
            .Select((uri, index) => new { uri, index })
            .GroupBy(x => x.index / batchSize)
            .Select(g => g.Select(x => x.uri).ToList());
        
        foreach (var batch in batches)
        {
            // Process batch in parallel
            var tasks = batch.Select(uri => Task.Run(() => ProcessVideoAsync(uri)));
            await Task.WhenAll(tasks);
            
            // Small delay between batches to avoid throttling
            await Task.Delay(TimeSpan.FromSeconds(1));
        }
    }
    
    /// <summary>
    /// Adaptive quality based on requirements
    /// </summary>
    public VFPFingerprintSource GetOptimizedSource(
        string videoPath,
        ProcessingTier tier)
    {
        // Note: VFPFingerprintSource is for specifying video file sources
        // Process video differently based on tier before fingerprinting
        return tier switch
        {
            ProcessingTier.Economy => new VFPFingerprintSource(videoPath)
            {
                CustomResolution = new Size(320, 240),
                FrameRate = 5,
                StopTime = TimeSpan.FromSeconds(30)
            },
            ProcessingTier.Standard => new VFPFingerprintSource(videoPath)
            {
                CustomResolution = new Size(640, 480),
                FrameRate = 10,
                StopTime = TimeSpan.FromSeconds(60)
            },
            ProcessingTier.Premium => new VFPFingerprintSource(videoPath)
            {
                CustomResolution = new Size(1280, 720),
                FrameRate = 15
            },
            _ => throw new ArgumentException("Invalid processing tier")
        };
    }
}

public enum ProcessingTier
{
    Economy,  // Lowest cost, reduced accuracy
    Standard, // Balanced cost/accuracy
    Premium   // Highest accuracy, higher cost
}

# 3. Cost Monitoring and Alerts

// EXAMPLE IMPLEMENTATION - Cost monitoring pattern
public class CostMonitor
{
    private readonly CloudCostTracker _costTracker;
    
    /// <summary>
    /// Track costs per operation
    /// </summary>
    public async Task<CostReport> TrackOperationCostAsync(
        Func<Task> operation,
        string operationType)
    {
        var startTime = DateTime.UtcNow;
        var startResources = await GetResourceUsageAsync();
        
        await operation();
        
        var endTime = DateTime.UtcNow;
        var endResources = await GetResourceUsageAsync();
        
        var cost = CalculateCost(startResources, endResources, endTime - startTime);
        
        // Log to monitoring system
        await _costTracker.LogCostAsync(new CostEntry
        {
            OperationType = operationType,
            Duration = endTime - startTime,
            ComputeCost = cost.ComputeCost,
            StorageCost = cost.StorageCost,
            TransferCost = cost.TransferCost,
            TotalCost = cost.TotalCost
        });
        
        // Alert if cost exceeds threshold
        if (cost.TotalCost > GetCostThreshold(operationType))
        {
            await SendCostAlertAsync(operationType, cost);
        }
        
        return cost;
    }
    
    /// <summary>
    /// Optimize based on cost patterns
    /// </summary>
    public async Task<CostOptimizationRecommendations> AnalyzeCostPatternsAsync(
        DateTime startDate,
        DateTime endDate)
    {
        var costs = await _costTracker.GetCostsAsync(startDate, endDate);
        
        var recommendations = new CostOptimizationRecommendations();
        
        // Analyze compute costs
        var avgComputeCost = costs.Average(c => c.ComputeCost);
        if (avgComputeCost > 100) // $100 threshold
        {
            recommendations.Add("Consider using spot instances for batch processing");
            recommendations.Add("Implement auto-scaling to reduce idle compute");
        }
        
        // Analyze storage costs
        var totalStorageCost = costs.Sum(c => c.StorageCost);
        if (totalStorageCost > 500) // $500 threshold
        {
            recommendations.Add("Move older fingerprints to archive storage");
            recommendations.Add("Implement compression for fingerprint storage");
            recommendations.Add("Review and delete unused fingerprints");
        }
        
        // Analyze transfer costs
        var totalTransferCost = costs.Sum(c => c.TransferCost);
        if (totalTransferCost > 200) // $200 threshold
        {
            recommendations.Add("Use CDN for frequently accessed fingerprints");
            recommendations.Add("Process videos in the same region as storage");
        }
        
        return recommendations;
    }
}

# Serverless Architecture Examples

# Complete Serverless Solution

public class ServerlessFingerprintingArchitecture
{
    /// <summary>
    /// API Gateway + Lambda + DynamoDB + S3
    /// </summary>
    public class ServerlessApi
    {
        // API endpoint handler
        public async Task<APIGatewayProxyResponse> HandleRequest(
            APIGatewayProxyRequest request,
            ILambdaContext context)
        {
            return request.HttpMethod switch
            {
                "POST" => await CreateFingerprintAsync(request, context),
                "GET" => await GetFingerprintAsync(request, context),
                "DELETE" => await DeleteFingerprintAsync(request, context),
                _ => new APIGatewayProxyResponse { StatusCode = 405 }
            };
        }
        
        private async Task<APIGatewayProxyResponse> CreateFingerprintAsync(
            APIGatewayProxyRequest request,
            ILambdaContext context)
        {
            var body = JsonSerializer.Deserialize<CreateFingerprintRequest>(request.Body);
            
            // Validate request
            if (string.IsNullOrEmpty(body?.VideoUrl))
            {
                return new APIGatewayProxyResponse
                {
                    StatusCode = 400,
                    Body = JsonSerializer.Serialize(new { error = "VideoUrl is required" })
                };
            }
            
            // Generate unique ID
            var fingerprintId = Guid.NewGuid().ToString();
            
            // Store request in DynamoDB
            await StoreRequestAsync(fingerprintId, body);
            
            // Queue for async processing
            await QueueForProcessingAsync(fingerprintId, body.VideoUrl);
            
            // Return immediate response
            return new APIGatewayProxyResponse
            {
                StatusCode = 202,
                Headers = new Dictionary<string, string>
                {
                    ["Location"] = $"/fingerprints/{fingerprintId}"
                },
                Body = JsonSerializer.Serialize(new
                {
                    id = fingerprintId,
                    status = "processing",
                    statusUrl = $"/fingerprints/{fingerprintId}/status"
                })
            };
        }
    }
    
    /// <summary>
    /// Event-driven processing with Step Functions
    /// </summary>
    public class EventDrivenProcessor
    {
        public async Task ProcessS3UploadEvent(S3Event s3Event, ILambdaContext context)
        {
            foreach (var record in s3Event.Records)
            {
                if (record.EventName.StartsWith("ObjectCreated"))
                {
                    var bucket = record.S3.Bucket.Name;
                    var key = record.S3.Object.Key;
                    
                    // Check if it's a video file
                    if (IsVideoFile(key))
                    {
                        // Start Step Functions execution
                        await StartWorkflowAsync(bucket, key);
                    }
                }
            }
        }
        
        private bool IsVideoFile(string key)
        {
            var videoExtensions = new[] { ".mp4", ".avi", ".mov", ".mkv", ".wmv" };
            return videoExtensions.Any(ext => key.EndsWith(ext, StringComparison.OrdinalIgnoreCase));
        }
    }
}

# Security Best Practices

# 1. Encryption and Access Control

public class SecureFingerprintStorage
{
    /// <summary>
    /// Encrypt fingerprints at rest and in transit
    /// </summary>
    public async Task<string> StoreSecurelyAsync(VFPFingerPrint fingerprint, string videoId)
    {
        // Encrypt fingerprint data
        var encryptedData = await EncryptDataAsync(fingerprint);
        
        // Store with encryption
        var storageClient = new BlobServiceClient(
            new Uri("https://storage.blob.core.windows.net"),
            new DefaultAzureCredential()); // Use managed identity
        
        var blobClient = storageClient
            .GetBlobContainerClient("secure-fingerprints")
            .GetBlobClient($"{videoId}.vfp.encrypted");
        
        var uploadOptions = new BlobUploadOptions
        {
            HttpHeaders = new BlobHttpHeaders
            {
                ContentType = "application/octet-stream"
            },
            Metadata = new Dictionary<string, string>
            {
                ["encrypted"] = "true",
                ["algorithm"] = "AES256",
                ["videoId"] = videoId
            }
        };
        
        await blobClient.UploadAsync(new BinaryData(encryptedData), uploadOptions);
        
        // Audit log
        await LogAccessAsync("STORE", videoId, "SUCCESS");
        
        return blobClient.Uri.ToString();
    }
    
    /// <summary>
    /// Implement role-based access control
    /// </summary>
    public async Task<bool> AuthorizeAccessAsync(string userId, string resource, string action)
    {
        // Check user permissions
        var permissions = await GetUserPermissionsAsync(userId);
        
        return action switch
        {
            "READ" => permissions.Contains("fingerprint:read"),
            "WRITE" => permissions.Contains("fingerprint:write"),
            "DELETE" => permissions.Contains("fingerprint:delete"),
            _ => false
        };
    }
}

# 2. Network Security

public class NetworkSecurityConfig
{
    /// <summary>
    /// Configure VPC endpoints for private communication
    /// </summary>
    public static void ConfigurePrivateEndpoints()
    {
        // Use VPC endpoints to avoid internet routing
        Environment.SetEnvironmentVariable("AWS_S3_ENDPOINT", "https://s3.vpc-endpoint.amazonaws.com");
        Environment.SetEnvironmentVariable("AZURE_STORAGE_ENDPOINT", "https://storage.privatelink.blob.core.windows.net");
    }
    
    /// <summary>
    /// Implement API throttling
    /// </summary>
    public class ApiThrottling
    {
        private readonly IMemoryCache _cache;
        
        public async Task<bool> CheckRateLimitAsync(string clientId)
        {
            var key = $"ratelimit:{clientId}";
            var requests = await _cache.GetOrCreateAsync(key, async entry =>
            {
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
                return 0;
            });
            
            if (requests >= 100) // 100 requests per minute
            {
                return false;
            }
            
            _cache.Set(key, requests + 1);
            return true;
        }
    }
}

# Performance Benchmarks

# Cloud Provider Comparison

Metric Azure Functions AWS Lambda Google Cloud Run
Cold Start 1-3 seconds 1-2 seconds 2-4 seconds
Warm Start 100-200ms 50-100ms 200-300ms
Max Execution Time 10 minutes 15 minutes 60 minutes
Max Memory 14 GB 10 GB 32 GB
Cost per Million Requests $0.20 $0.20 $0.40
Cost per GB-second $0.000016 $0.0000166 $0.0000025

# Processing Performance

public class PerformanceBenchmarks
{
    public static async Task RunBenchmarksAsync()
    {
        var results = new List<BenchmarkResult>();
        
        // Test different configurations
        var configurations = new[]
        {
            new { Resolution = new Size(320, 240), FrameRate = 5, Label = "Economy" },
            new { Resolution = new Size(640, 480), FrameRate = 10, Label = "Standard" },
            new { Resolution = new Size(1280, 720), FrameRate = 15, Label = "Premium" }
        };
        
        foreach (var config in configurations)
        {
            var sw = Stopwatch.StartNew();
            
            var source = new VFPFingerprintSource("test.mp4")
            {
                CustomResolution = config.Resolution,
                FrameRate = config.FrameRate
            };
            
            var fingerprint = await VFPAnalyzer.GetComparingFingerprintForVideoFileAsync(source);
            
            sw.Stop();
            
            results.Add(new BenchmarkResult
            {
                Configuration = config.Label,
                ProcessingTime = sw.Elapsed,
                FingerprintSize = fingerprint?.Data?.Length ?? 0,
                MemoryUsed = GC.GetTotalMemory(false) / (1024 * 1024) // MB
            });
        }
        
        // Display results
        foreach (var result in results)
        {
            Console.WriteLine($"{result.Configuration}:");
            Console.WriteLine($"  Processing Time: {result.ProcessingTime.TotalSeconds:F2}s");
            Console.WriteLine($"  Fingerprint Size: {result.FingerprintSize / 1024}KB");
            Console.WriteLine($"  Memory Used: {result.MemoryUsed}MB");
        }
    }
}

# Troubleshooting Cloud Deployments

# Common Issues and Solutions

  1. Lambda Timeout Issues

    • Increase timeout settings
    • Optimize video resolution and frame rate
    • Use Step Functions for long-running processes
  2. Storage Access Errors

    • Verify IAM roles and permissions
    • Check network connectivity and VPC settings
    • Ensure proper SDK configuration
  3. Cost Overruns

    • Implement proper lifecycle policies
    • Use spot instances for batch processing
    • Monitor and alert on unusual usage patterns
  4. Performance Degradation

    • Scale compute resources appropriately
    • Implement caching strategies
    • Use CDN for frequently accessed content

# Summary

Cloud-based video fingerprinting offers unprecedented scalability and cost-effectiveness. Key takeaways:

  • Choose the right service: Serverless for variable loads, containers for consistent processing
  • Optimize costs: Use tiered storage, spot instances, and intelligent caching
  • Ensure security: Encrypt data, use managed identities, implement access controls
  • Monitor performance: Track costs, set up alerts, and continuously optimize
  • Plan for scale: Design for distributed processing from the start

# Next Steps

# Additional Resources