### Perform CRUD operations with Pulsy.EKV.Client in C# Source: https://github.com/pulsy-global/pulsy-ekv/blob/main/Pulsy.EKV.Client/README.md Demonstrates how to initialize an EkvClient, select a namespace, and perform basic key-value operations including Put, Get, and Delete. It requires the Pulsy.EKV.Client NuGet package and a running Pulsy EKV instance. ```csharp using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("my-namespace"); await ns.PutAsync("key", "value"u8.ToArray()); var value = await ns.GetAsync("key"); await ns.DeleteAsync("key"); ``` -------------------------------- ### Pulsy EKV Server Configuration Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Provides an example of the `appsettings.json` configuration file for a Pulsy EKV server. It details settings for Kestrel endpoints, node identification, NATS clustering, storage backends (Local and S3), connection pooling, and various operational limits. ```json { "Kestrel": { "Endpoints": { "Grpc": { "Url": "http://0.0.0.0:8080", "Protocols": "Http2" } } }, "Node": { "Id": "ekv-node-1", "DataPath": "/data", "GrpcEndpoint": "http://ekv-node-1:8080", "ShutdownTimeoutSeconds": 120 }, "Nats": { "Url": "nats://nats:4222" }, "Cluster": { "ClusterMode": true, "LeaseTtlSeconds": 30, "LeaseRenewSeconds": 20, "StatusTtlSeconds": 15, "StatusIntervalSeconds": 10, "LeaderTtlSeconds": 15, "LeaderRenewSeconds": 10, "ClusterPollSeconds": 5, "DrainTimeoutSeconds": 60 }, "Backends": { "default": { "Type": "Local" }, "s3": { "Type": "S3", "Bucket": "ekv", "Region": "us-east-1", "Endpoint": "http://minio:9000", "AccessKeyId": "minioadmin", "SecretAccessKey": "minioadmin", "AllowHttp": true } }, "Pool": { "MaxOpen": 100, "IdleTimeoutSeconds": 300, "Compression": "zstd", "L0MaxSsts": 64, "L0SstSizeBytes": 33554432, "MaxUnflushedBytes": 134217728, "WalEnabled": true, "AwaitDurable": false }, "Limits": { "MaxValueBytes": 409600, "MaxKeyBytes": 512, "MaxGrpcMessageBytes": 67108864 } } ``` -------------------------------- ### MultiGet Multiple Keys (C#) Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Retrieves multiple keys in a single request, returning a dictionary containing only the keys that were successfully found. This is more efficient than making individual Get requests for multiple items. Includes an extension method for direct string retrieval. ```csharp using Pulsy.EKV.Client; using System.Text; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("users"); // Store some test data await ns.BatchAsync(b => { b.Put("user:1", "{\"name\":\"Alice\"}"); b.Put("user:2", "{\"name\":\"Bob\"}"); b.Put("user:3", "{\"name\":\"Charlie\"}"); }); // Fetch multiple keys at once (returns only found keys) IReadOnlyDictionary results = await ns.MultiGetAsync( new[] { "user:1", "user:2", "user:99" } // user:99 doesn't exist ); Console.WriteLine($"Found {results.Count} keys"); // Output: Found 2 keys foreach (var (key, value) in results) { Console.WriteLine($"{key}: {Encoding.UTF8.GetString(value)}"); } // Output: // user:1: {"name":"Alice"} // user:2: {"name":"Bob"} // Check if specific key was found bool hasUser99 = results.ContainsKey("user:99"); // false // Using string extension for automatic UTF-8 decoding IReadOnlyDictionary stringResults = await ns.MultiGetStringsAsync( new[] { "user:1", "user:2", "user:3" } ); foreach (var (key, json) in stringResults) { Console.WriteLine($"{key}: {json}"); } ``` -------------------------------- ### GET /namespaces/{namespace}/keys/{key} Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Retrieves a specific value associated with a key from a namespace. Returns null if the key does not exist or has expired. ```APIDOC ## GET /namespaces/{namespace}/keys/{key} ### Description Retrieves a value by key from the namespace. Returns null if the key does not exist or has expired. ### Method GET ### Endpoint /namespaces/{namespace}/keys/{key} ### Parameters #### Path Parameters - **namespace** (string) - Required - The target namespace. - **key** (string) - Required - The unique identifier for the value. ### Response #### Success Response (200) - **value** (byte[]) - The binary data associated with the key. #### Response Example { "value": "eyJuYW1lIjoiQWxpY2UifQ==" } ``` -------------------------------- ### Get Value by Key (C#) Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Retrieves a value associated with a specific key from a namespace. Returns the value as a byte array or null if the key is not found or has expired. Includes an extension method for direct string retrieval and handles non-existent keys gracefully. ```csharp using Pulsy.EKV.Client; using System.Text; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("users"); // Get a value (returns byte[] or null) byte[]? result = await ns.GetAsync("user:123"); if (result != null) { string json = Encoding.UTF8.GetString(result); Console.WriteLine($"Found: {json}"); } else { Console.WriteLine("Key not found"); } // Using string extension method string? value = await ns.GetStringAsync("user:123"); Console.WriteLine(value ?? "Not found"); // Handle non-existent key byte[]? missing = await ns.GetAsync("non-existent-key"); // missing is null ``` -------------------------------- ### Delete Key (C#) Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Removes a key-value pair from a namespace. The operation is safe and does not throw an error if the key does not exist. The example demonstrates storing a value, verifying its existence, deleting it, and then verifying its absence. ```csharp using Pulsy.EKV.Client; using System.Text; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("users"); // Store a value await ns.PutAsync("key-to-delete", "value"u8.ToArray()); // Verify it exists var before = await ns.GetAsync("key-to-delete"); Console.WriteLine($"Before delete: {(before != null ? "exists" : "missing")}"); // Delete the key await ns.DeleteAsync("key-to-delete"); // Verify deletion var after = await ns.GetAsync("key-to-delete"); Console.WriteLine($"After delete: {(after != null ? "exists" : "missing")}"); // Output: After delete: missing // Deleting non-existent key is safe (no error thrown) await ns.DeleteAsync("already-deleted-key"); ``` -------------------------------- ### Deploy Pulsy EKV Cluster with Docker Compose Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt This Docker Compose configuration sets up a multi-node Pulsy EKV cluster. It includes NATS for coordination, MinIO for S3-compatible storage, and configures multiple EKV nodes for distributed operation. Dependencies ensure services start in the correct order. ```yaml services: nats: image: nats:latest ports: - "4222:4222" command: ["-js"] minio: image: minio/minio:latest ports: - "9000:9000" - "9001:9001" environment: MINIO_ROOT_USER: minioadmin MINIO_ROOT_PASSWORD: minioadmin command: server /data --console-address ":9001" minio-init: image: minio/mc:latest depends_on: - minio entrypoint: > /bin/sh -c " until mc alias set local http://minio:9000 minioadmin minioadmin; do sleep 1; done; mc mb local/ekv --ignore-existing; " ekv-node-1: image: ghcr.io/pulsy-global/ekv-node:latest hostname: ekv-node-1 environment: Kestrel__Endpoints__Grpc__Url: http://0.0.0.0:8080 Node__Id: ekv-node-1 Node__DataPath: /data Node__GrpcEndpoint: http://ekv-node-1:8080 Cluster__ClusterMode: "true" Nats__Url: nats://nats:4222 Backends__s3__Endpoint: http://minio:9000 Backends__s3__Bucket: ekv Backends__s3__AccessKeyId: minioadmin Backends__s3__SecretAccessKey: minioadmin Backends__s3__AllowHttp: "true" ports: - "8081:8080" depends_on: - nats - minio-init ekv-node-2: image: ghcr.io/pulsy-global/ekv-node:latest hostname: ekv-node-2 environment: Kestrel__Endpoints__Grpc__Url: http://0.0.0.0:8080 Node__Id: ekv-node-2 Node__DataPath: /data Node__GrpcEndpoint: http://ekv-node-2:8080 Cluster__ClusterMode: "true" Nats__Url: nats://nats:4222 Backends__s3__Endpoint: http://minio:9000 Backends__s3__Bucket: ekv Backends__s3__AccessKeyId: minioadmin Backends__s3__SecretAccessKey: minioadmin Backends__s3__AllowHttp: "true" ports: - "8082:8080" depends_on: - nats - minio-init ``` -------------------------------- ### Pulsy EKV gRPC Protocol Definition Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt This protobuf definition outlines the gRPC API for Pulsy EKV, covering key-value store operations like Get, Put, Delete, Batch, MultiGet, and Scan. It also includes definitions for administrative operations such as namespace management. ```protobuf syntax = "proto3"; package ekv; // Key-Value Store Operations service EkvStore { rpc Get (GetRequest) returns (GetResponse); rpc Put (PutRequest) returns (PutResponse); rpc Delete (DeleteRequest) returns (DeleteResponse); rpc Batch (BatchRequest) returns (BatchResponse); rpc MultiGet (MultiGetRequest) returns (MultiGetResponse); rpc Scan (ScanRequest) returns (stream ScanEntry); } message GetRequest { string namespace = 1; string key = 2; } message GetResponse { bool found = 1; bytes value = 2; } message PutRequest { string namespace = 1; string key = 2; bytes value = 3; optional int64 ttl_ms = 4; } message BatchRequest { string namespace = 1; repeated BatchOperation ops = 2; } message BatchOperation { BatchOpType type = 1; // PUT or DELETE string key = 2; bytes value = 3; optional int64 ttl_ms = 4; } message ScanRequest { string namespace = 1; optional string prefix = 2; int32 limit = 5; // 0 = unlimited optional string cursor = 6; // exclusive start key for pagination } // Admin Operations service EkvAdmin { rpc CreateNamespace (CreateNamespaceRequest) returns (CreateNamespaceResponse); rpc GetNamespace (GetNamespaceRequest) returns (GetNamespaceResponse); rpc ListNamespaces (ListNamespacesRequest) returns (ListNamespacesResponse); rpc UpdateNamespace (UpdateNamespaceRequest) returns (UpdateNamespaceResponse); rpc DeleteNamespace (DeleteNamespaceRequest) returns (DeleteNamespaceResponse); rpc HibernateNamespace (HibernateNamespaceRequest) returns (HibernateNamespaceResponse); } message CreateNamespaceRequest { string name = 1; string backend = 2; // "default", "s3", etc. } ``` -------------------------------- ### Initialize Pulsy EKV Client Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Demonstrates how to instantiate the EkvClient with default settings or custom configuration including retry policies and endpoint settings. ```csharp using Pulsy.EKV.Client; using Pulsy.EKV.Client.Configuration; using var client = new EkvClient(); using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://ekv-node-1:8080", MaxMessageBytes = 64 * 1024 * 1024, RetryMaxAttempts = 5, RetryInitialBackoffMs = 100, RetryMaxBackoffMs = 5000, RetryBackoffMultiplier = 2.0 }); var ns = client.Namespace("my-namespace"); var admin = client.Admin(); ``` -------------------------------- ### Scan with Prefix and Pagination in C# Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Demonstrates how to scan keys matching a specific prefix with pagination support using the Pulsy EKV client in C#. It shows fetching the first page of results and then retrieving subsequent pages using a cursor. Dependencies include the Pulsy.EKV.Client library. ```csharp using Pulsy.EKV.Client; using Pulsy.EKV.Client.Models; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("logs"); // Store test data await ns.BatchAsync(b => { b.Put("log:2024-01-01:001", "Event 1"); b.Put("log:2024-01-01:002", "Event 2"); b.Put("log:2024-01-02:001", "Event 3"); b.Put("log:2024-01-02:002", "Event 4"); b.Put("log:2024-01-02:003", "Event 5"); b.Put("other:key", "Different prefix"); }); // Scan first page with limit ScanResult page1 = await ns.ScanPrefixAsync( prefix: "log:2024-01-02:", limit: 2 ); Console.WriteLine($"Page 1: {page1.Items.Count} items, HasMore: {page1.HasMore}"); foreach (KvEntry entry in page1.Items) { Console.WriteLine($" {entry.Key}: {entry.StringValue}"); } // Output: // Page 1: 2 items, HasMore: True // log:2024-01-02:001: Event 3 // log:2024-01-02:002: Event 4 // Fetch next page using cursor if (page1.HasMore && page1.NextCursor != null) { ScanResult page2 = await ns.ScanPrefixAsync( prefix: "log:2024-01-02:", limit: 2, cursor: page1.NextCursor ); Console.WriteLine($"Page 2: {page2.Items.Count} items, HasMore: {page2.HasMore}"); foreach (var entry in page2.Items) { Console.WriteLine($" {entry.Key}: {entry.StringValue}"); } } // Output: // Page 2: 1 items, HasMore: False // log:2024-01-02:003: Event 5 ``` -------------------------------- ### Perform Put Operations with TTL Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Shows how to store key-value pairs using byte arrays or strings, including optional TTL settings for automatic key expiration. ```csharp using Pulsy.EKV.Client; using System.Text; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("users"); byte[] userData = Encoding.UTF8.GetBytes("{\"id\":123,\"name\":\"Alice\"}"); await ns.PutAsync("user:123", userData); await ns.PutAsync("session:abc", "token-data"u8.ToArray(), TimeSpan.FromHours(1)); await ns.PutAsync("temp-key", Encoding.UTF8.GetBytes("temporary"), TimeSpan.FromSeconds(2)); await ns.PutAsync("user:456", "{\"id\":456,\"name\":\"Bob\"}"); await ns.PutAsync("cache:key", "cached-value", TimeSpan.FromMinutes(15)); ``` -------------------------------- ### Stream All with Async Enumerable in C# Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Illustrates how to efficiently stream all keys matching a prefix using `IAsyncEnumerable` in C#. This method is suitable for iterating over large datasets without consuming excessive memory. It requires the Pulsy.EKV.Client library and supports cancellation. ```csharp using Pulsy.EKV.Client; using Pulsy.EKV.Client.Models; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("events"); // Store test data await ns.BatchAsync(b => { for (int i = 1; i <= 100; i++) { b.Put($"event:{i:D4}", ${"{i}","type":"click"}}); } }); // Stream all matching keys (memory-efficient for large datasets) int count = 0; await foreach (KvEntry entry in ns.ScanPrefixAllAsync("event:")) { Console.WriteLine($"{entry.Key}: {entry.StringValue}"); count++; // Can break early if needed if (count >= 10) { Console.WriteLine("... (truncated)"); break; } } // Process all events with LINQ-style operations var eventCount = 0; await foreach (var entry in ns.ScanPrefixAllAsync("event:")) { eventCount++; } Console.WriteLine($"Total events: {eventCount}"); // With cancellation support using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); await foreach (var entry in ns.ScanPrefixAllAsync("event:", cts.Token)) { // Process entry } ``` -------------------------------- ### POST /namespaces/{namespace}/multiget Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Retrieves multiple keys in a single request, returning a map of keys that were successfully found. ```APIDOC ## POST /namespaces/{namespace}/multiget ### Description Retrieves multiple keys in a single request. Returns a dictionary containing only the keys that were found. ### Method POST ### Endpoint /namespaces/{namespace}/multiget ### Request Body - **keys** (array) - Required - List of keys to retrieve. ### Response #### Success Response (200) - **results** (object) - Dictionary where keys are the requested keys and values are the retrieved data. #### Response Example { "user:1": "{\"name\":\"Alice\"}", "user:2": "{\"name\":\"Bob\"}" } ``` -------------------------------- ### Manage Namespaces via Admin API Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Covers namespace lifecycle management including creation, retrieval, listing, updating, hibernating, and deletion operations. ```csharp using Pulsy.EKV.Client; using Pulsy.EKV.Client.Models; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var admin = client.Admin(); await admin.CreateNamespaceAsync(new NamespaceInfo { Name = "users", Backend = "default" }); await admin.CreateNamespaceAsync(new NamespaceInfo { Name = "sessions", Backend = "s3" }); await admin.EnsureNamespaceAsync(new NamespaceInfo { Name = "cache", Backend = "default" }); var info = await admin.GetNamespaceAsync("users"); var namespaces = await admin.ListNamespacesAsync(); await admin.UpdateNamespaceAsync(new NamespaceInfo { Name = "users", Backend = "s3" }); await admin.HibernateNamespaceAsync("sessions"); await admin.DeleteNamespaceAsync("cache"); ``` -------------------------------- ### gRPC EkvStore API Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Operations for interacting with the key-value store, including retrieval, storage, and batch processing. ```APIDOC ## gRPC EkvStore ### Description Provides methods for performing CRUD and batch operations on keys within specific namespaces. ### Method gRPC (Unary/Stream) ### Endpoint ekv.EkvStore ### Request Body - **GetRequest** (message) - Contains namespace and key - **PutRequest** (message) - Contains namespace, key, value, and optional ttl_ms - **BatchRequest** (message) - Contains namespace and a list of BatchOperation objects ### Response #### Success Response - **GetResponse** (message) - Returns found status and value bytes - **PutResponse** (message) - Confirmation of storage operation - **BatchResponse** (message) - Confirmation of batch operation status ``` -------------------------------- ### Batch Operations (C#) Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Executes multiple Put and Delete operations atomically within a single request for improved efficiency. Supports putting byte arrays, strings, and setting Time-To-Live (TTL) for keys. Also demonstrates deleting keys as part of the batch. ```csharp using Pulsy.EKV.Client; using System.Text; using var client = new EkvClient(new EkvClientOptions { Endpoint = "http://localhost:8080" }); var ns = client.Namespace("inventory"); // Batch write multiple keys await ns.BatchAsync(batch => { // Put operations with byte arrays batch.Put("product:1", Encoding.UTF8.GetBytes("{\"name\":\"Widget\",\"stock\":100}")); batch.Put("product:2", Encoding.UTF8.GetBytes("{\"name\":\"Gadget\",\"stock\":50}")); batch.Put("product:3", Encoding.UTF8.GetBytes("{\"name\":\"Gizmo\",\"stock\":25}")); // Put with TTL batch.Put("promo:flash-sale", "active"u8.ToArray(), TimeSpan.FromHours(2)); // Using string extensions batch.Put("category:electronics", "enabled"); batch.Put("cache:products-list", "[1,2,3]", TimeSpan.FromMinutes(5)); // Delete operations batch.Delete("product:discontinued"); batch.Delete("old-key"); }); // Verify batch results var p1 = await ns.GetStringAsync("product:1"); var p2 = await ns.GetStringAsync("product:2"); Console.WriteLine($"Product 1: {p1}"); Console.WriteLine($"Product 2: {p2}"); ``` -------------------------------- ### POST /namespaces/{namespace}/batch Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Executes multiple Put and Delete operations atomically in a single request for improved efficiency. ```APIDOC ## POST /namespaces/{namespace}/batch ### Description Executes multiple Put and Delete operations atomically in a single request. ### Method POST ### Endpoint /namespaces/{namespace}/batch ### Request Body - **operations** (array) - Required - List of operations containing type (PUT/DELETE), key, and optional value/ttl. ### Request Example { "operations": [ { "type": "PUT", "key": "product:1", "value": "..." }, { "type": "DELETE", "key": "old-key" } ] } ``` -------------------------------- ### gRPC EkvAdmin API Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Administrative operations for managing namespaces within the Pulsy EKV cluster. ```APIDOC ## gRPC EkvAdmin ### Description Administrative interface for creating, listing, and managing namespaces in the cluster. ### Method gRPC (Unary) ### Endpoint ekv.EkvAdmin ### Request Body - **CreateNamespaceRequest** (message) - Requires name and backend type (e.g., "s3") - **DeleteNamespaceRequest** (message) - Requires namespace name ### Response #### Success Response - **CreateNamespaceResponse** (message) - Confirmation of namespace creation - **ListNamespacesResponse** (message) - Returns list of existing namespaces ``` -------------------------------- ### DELETE /namespaces/{namespace}/keys/{key} Source: https://context7.com/pulsy-global/pulsy-ekv/llms.txt Removes a key-value pair from the specified namespace. The operation is idempotent and succeeds even if the key does not exist. ```APIDOC ## DELETE /namespaces/{namespace}/keys/{key} ### Description Removes a key-value pair from the namespace. The operation succeeds even if the key does not exist. ### Method DELETE ### Endpoint /namespaces/{namespace}/keys/{key} ### Parameters #### Path Parameters - **namespace** (string) - Required - The target namespace. - **key** (string) - Required - The key to remove. ### Response #### Success Response (200) - **status** (string) - Confirmation of operation completion. ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.