diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86bd1eb4..419d65f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,18 +2,71 @@ Thank you for your interest in contributing to the Valkey GLIDE documentation! -## Branches Overview +## Deployment Model -| Branch | Purpose | -| -------- | -------------------------------------------------------------------------------- | -| `main` | Integration branch — all PRs target here | -| `public` | Production — deploys the live site at [glide.valkey.io](https://glide.valkey.io) | +We use two branches: -In general: +| Branch | Purpose | +| -------- | ----------------------------------------------------------------------------------------------------- | +| `main` | Integration branch. Source of truth. All PRs target here. Content on `main` is awaiting release. | +| `public` | Production branch. deploys the live site at [glide.valkey.io](https://glide.valkey.io) on every push. | -- Open all PRs against `main`. -- On release day, `main` is merged onto `public` triggering a deployment. -- Only urgent fixes are accepted directly into `public`. +All documentation changes flow through `main` first, then are promoted to `public` on release. + +### Contribution Flow + +1. Open your PR against `main`. +2. Once approved and CI passes, merge to `main`. Merge early and often to avoid big-bang release-day merges. +3. Content sits on `main` until the next release. + +### Release Process + +`main` is the source of truth. On release day, a maintainer resets `public` to `main`'s HEAD and force-pushes: + +```bash +git fetch origin +git push origin +origin/main:public +``` + +Any contents on `main` that isn't ready to go live should be disabled (see [Unreleased Content](#unreleased-content)). + +### Applying a Hotfix + +For urgent fixes to the live site that can't wait for the next release: + +1. Open a PR targeting `public`. +2. Once merged, deployment will start automatically. +3. **Backport the same change to `main` immediately**. Because releases reset `public` to `main` HEAD, any hotfix that isn't on `main` before the next release will be erased. + +### Unreleased Content + +Content that has landed on `main` but isn't ready for release yet must be marked as draft so it doesn't appear on the live site when `public` is next updated. + +**Draft Page** — set `draft: true` in frontmatter. The page is excluded from production builds but visible in `pnpm dev`. Always include a `draft-reason` so releases can locate what to unflip. + +``` +--- +title: My New Feature +description: ... +draft: true +draft-reason: For release 2.5 +--- +``` + +Remove the page from sidebar entry in `astro.config.mjs` as it will fail the build if linked explicitly. + +**Draft Section** (e.g., one language's example tab) — wrap the section in the `` component. Always include a `draft-reason`. + +```mdx +import Draft from "@components/Draft.astro"; + + + ... + + ... + + +``` ## Getting Started diff --git a/src/components/Draft.astro b/src/components/Draft.astro new file mode 100644 index 00000000..5168cda2 --- /dev/null +++ b/src/components/Draft.astro @@ -0,0 +1,10 @@ +--- +/** + * Inline equivalent of Starlight's page-level `draft: true` frontmatter: + * hides its children in production builds; shows them in dev mode. + * + * To release the content, delete the wrapping tags. + */ +--- + +{import.meta.env.DEV && } diff --git a/src/content/docs/concepts/client-features/client-side-caching.mdx b/src/content/docs/concepts/client-features/client-side-caching.mdx index ba8f5d27..7862473e 100644 --- a/src/content/docs/concepts/client-features/client-side-caching.mdx +++ b/src/content/docs/concepts/client-features/client-side-caching.mdx @@ -535,11 +535,10 @@ Multiple clients can share the same cache instance by passing the same `ClientSi ## Server-Assisted Invalidation Server-assisted invalidation can be enabled in the client side configuration by setting server assisted to true. -You do not need to call [`CLIENT TRACKING`](https://valkey.io/commands/client-tracking/) yourself; GLIDE issues `CLIENT TRACKING ON BCAST` on your behalf during connection setup. +You do not need to call [`CLIENT TRACKING`](https://valkey.io/commands/client-tracking/) yourself; GLIDE issues `CLIENT TRACKING ON BCAST` on your behalf during connection setup. Once enabled, the client receives push invalidation messages from the server whenever a cached key is modified, eliminating the staleness window inherent in TTL-only mode — the server notifies the client immediately when data changes, and the client removes the stale entry from its local cache. - ### Enabling server-assisted mode diff --git a/src/content/docs/how-to/connections/resilience-best-practices.mdx b/src/content/docs/how-to/connections/resilience-best-practices.mdx index b744d702..5c3860dc 100644 --- a/src/content/docs/how-to/connections/resilience-best-practices.mdx +++ b/src/content/docs/how-to/connections/resilience-best-practices.mdx @@ -12,80 +12,75 @@ This guide covers configuration and operational practices that help Valkey GLIDE A resilient client combines request timeout, inflight limiting, and the circuit breaker: - - -```python -from glide import ( - GlideClusterClient, - GlideClusterClientConfiguration, - ClientCircuitBreakerConfiguration, - NodeAddress, -) - -config = GlideClusterClientConfiguration( - addresses=[NodeAddress(host="localhost", port=6379)], - request_timeout=500, - inflight_requests_limit=1000, - client_circuit_breaker=ClientCircuitBreakerConfiguration(), -) -client = await GlideClusterClient.create(config) -``` - - - - -```java -import glide.api.GlideClusterClient; -import glide.api.models.configuration.GlideClusterClientConfiguration; -import glide.api.models.configuration.ClientCircuitBreakerConfiguration; -import glide.api.models.configuration.NodeAddress; - -GlideClusterClient client = GlideClusterClient.createClient( - GlideClusterClientConfiguration.builder() - .address(NodeAddress.builder().host("localhost").port(6379).build()) - .requestTimeout(500) - .inflightRequestsLimit(1000) - .clientCircuitBreakerConfiguration( - ClientCircuitBreakerConfiguration.builder().build()) - .build() -).get(); -``` - - - - -```typescript -import { GlideClusterClient } from "@valkey/valkey-glide"; - -const client = await GlideClusterClient.createClient({ - addresses: [{ host: "localhost", port: 6379 }], - requestTimeout: 500, - inflightRequestsLimit: 1000, - clientCircuitBreaker: {}, -}); -``` - - - - -```go -import ( - "time" - - glide "github.com/valkey-io/valkey-glide/go/v2" - "github.com/valkey-io/valkey-glide/go/v2/config" -) - -clientConfig := config.NewClusterClientConfiguration(). - WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). - WithRequestTimeout(500 * time.Millisecond). - WithInflightRequestsLimit(1000). - WithClientCircuitBreaker(&config.ClientCircuitBreakerConfiguration{}) - -client, err := glide.NewClusterClient(clientConfig) -``` - - + + ```python + from glide import ( + GlideClusterClient, + GlideClusterClientConfiguration, + ClientCircuitBreakerConfiguration, + NodeAddress, + ) + + config = GlideClusterClientConfiguration( + addresses=[NodeAddress(host="localhost", port=6379)], + request_timeout=500, + inflight_requests_limit=1000, + client_circuit_breaker=ClientCircuitBreakerConfiguration(), + ) + client = await GlideClusterClient.create(config) + ``` + + + + ```java + import glide.api.GlideClusterClient; + import glide.api.models.configuration.GlideClusterClientConfiguration; + import glide.api.models.configuration.ClientCircuitBreakerConfiguration; + import glide.api.models.configuration.NodeAddress; + + GlideClusterClient client = GlideClusterClient.createClient( + GlideClusterClientConfiguration.builder() + .address(NodeAddress.builder().host("localhost").port(6379).build()) + .requestTimeout(500) + .inflightRequestsLimit(1000) + .clientCircuitBreakerConfiguration( + ClientCircuitBreakerConfiguration.builder().build()) + .build() + ).get(); + ``` + + + + ```typescript + import { GlideClusterClient } from "@valkey/valkey-glide"; + + const client = await GlideClusterClient.createClient({ + addresses: [{ host: "localhost", port: 6379 }], + requestTimeout: 500, + inflightRequestsLimit: 1000, + clientCircuitBreaker: {}, + }); + ``` + + + + ```go + import ( + "time" + + glide "github.com/valkey-io/valkey-glide/go/v2" + "github.com/valkey-io/valkey-glide/go/v2/config" + ) + + clientConfig := config.NewClusterClientConfiguration(). + WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379}). + WithRequestTimeout(500 * time.Millisecond). + WithInflightRequestsLimit(1000). + WithClientCircuitBreaker(&config.ClientCircuitBreakerConfiguration{}) + + client, err := glide.NewClusterClient(clientConfig) + ``` + ## Single Client Instance @@ -111,11 +106,11 @@ The `inflightRequestsLimit` controls how many concurrent requests a single clien **Key points:** -- The inflight limit should accommodate your expected concurrency: at throughput `T` ops/s with average latency `L` seconds, you need roughly `T × L` inflight slots (e.g., 50k ops/s at 1ms = ~50 slots, 50k ops/s at 10ms = ~500 slots). -- When the limit is reached, new requests block until a slot frees up. The circuit breaker (if enabled) rejects before this blocking occurs. -- If using Java with `ForkJoinPool.managedBlock()` (common in Akka, Play, or reactive frameworks), set the limit to 2-3x your thread pool size. This prevents thread explosion where `managedBlock()` spawns compensating threads faster than requests complete. -- If you see "inflight requests limit reached" warnings in logs, either increase the limit or reduce concurrency at the application level. -- Setting the limit too high risks memory pressure under sustained backpressure. The default of 1000 is appropriate for most workloads. +* The inflight limit should accommodate your expected concurrency: at throughput `T` ops/s with average latency `L` seconds, you need roughly `T × L` inflight slots (e.g., 50k ops/s at 1ms = ~50 slots, 50k ops/s at 10ms = ~500 slots). +* When the limit is reached, new requests block until a slot frees up. The circuit breaker (if enabled) rejects before this blocking occurs. +* If using Java with `ForkJoinPool.managedBlock()` (common in Akka, Play, or reactive frameworks), set the limit to 2-3x your thread pool size. This prevents thread explosion where `managedBlock()` spawns compensating threads faster than requests complete. +* If you see "inflight requests limit reached" warnings in logs, either increase the limit or reduce concurrency at the application level. +* Setting the limit too high risks memory pressure under sustained backpressure. The default of 1000 is appropriate for most workloads. ## Thread Pool Sizing (Java) @@ -168,11 +163,11 @@ See the dedicated [Circuit Breaker guide](/how-to/connections/circuit-breaker) f **Key points:** -- Enable CB for workloads where thread exhaustion is a concern during outages. -- The CB rejects before the request reaches the network, so rejections are near-zero-cost. -- Enable `countTimeouts` to protect against dead or unreachable nodes. In cluster mode, dead nodes surface primarily as timeouts, so this flag is required for the CB to trip on sustained node outages. -- Recovery is optimistic: in HalfOpen, all traffic is allowed through. After consecutive successes the breaker closes. If a failure occurs, it returns to Open. -- When the CB rejects, handle the `CircuitBreakerException`/`CircuitBreakerError` in your application by returning a cached or degraded response, or signaling the caller to retry after a delay. +* Enable CB for workloads where thread exhaustion is a concern during outages. +* The CB rejects before the request reaches the network, so rejections are near-zero-cost. +* Enable `countTimeouts` to protect against dead or unreachable nodes. In cluster mode, dead nodes surface primarily as timeouts, so this flag is required for the CB to trip on sustained node outages. +* Recovery is optimistic: in HalfOpen, all traffic is allowed through. After consecutive successes the breaker closes. If a failure occurs, it returns to Open. +* When the CB rejects, handle the `CircuitBreakerException`/`CircuitBreakerError` in your application by returning a cached or degraded response, or signaling the caller to retry after a delay. ## Standalone vs Cluster