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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/static-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ jobs:
- name: Check formatting
run: pnpm format:check

- name: Check language tab ordering
run: pnpm check:tab-order

- name: Build (will also check for internal links)
run: pnpm build

Expand Down
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ Per-language code examples use Starlight tabs synced across pages:
```

- Tab labels (in this order): `Python`, `Java`, `Node`, `Go`, `PHP`, `C#`, `Ruby`
- Languages a page doesn't cover are simply omitted; the remaining tabs keep this relative order
- This order is enforced by `pnpm check:tab-order` (run in CI). The check lives at `scripts/check-tab-order.mjs` — update the `CANONICAL` array there if the order ever changes
- Examples across tabs on the same page should be equivalent: same key names, values, hosts/ports, and flow

## Commit Requirements
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"format:check:non-mdx": "prettier --check .",
"format:check:mdx": "bash scripts/check-format.sh",
"format:check": "pnpm run format:check:non-mdx && pnpm run format:check:mdx",
"check:tab-order": "node scripts/check-tab-order.mjs",
"test-deploy": "pnpm build && ./test-deploy.sh"
},
"dependencies": {
Expand Down
124 changes: 124 additions & 0 deletions scripts/check-tab-order.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env node
// Validates that language <TabItem> groups in the docs follow the canonical
// ordering defined in AGENTS.md. Language tabs that are absent from a group are
// simply skipped; a group is a violation only when the RELATIVE order of the
// languages it does contain differs from the canonical order.
//
// Run: node scripts/check-tab-order.mjs (also exposed as `pnpm check:tab-order`)
// Exits non-zero and prints file:line for every offending <Tabs> group.

import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";
import { fileURLToPath } from "node:url";

const ROOT = fileURLToPath(new URL("..", import.meta.url));
const DOCS_DIR = join(ROOT, "src/content/docs");

// Canonical order — keep in sync with the "Language Tabs" section of AGENTS.md.
const CANONICAL = ["Python", "Java", "Node", "Go", "PHP", "C#", "Ruby"];
const RANK = new Map(CANONICAL.map((lang, i) => [lang, i]));

// Normalize label variants to their canonical key.
function normalize(label) {
if (label === "Node.js") return "Node";
return label;
}

function isLanguage(label) {
return RANK.has(normalize(label));
}

function mdxFiles(dir) {
const out = [];
for (const entry of readdirSync(dir)) {
const full = join(dir, entry);
if (statSync(full).isDirectory()) out.push(...mdxFiles(full));
else if (entry.endsWith(".mdx") || entry.endsWith(".md")) out.push(full);
}
return out;
}

const ITEM_RE = /<TabItem\b[^>]*\blabel=(?:"([^"]*)"|'([^']*)')/g;

// Parse a file into a list of tab groups. Uses a stack so that a <TabItem>
// is attributed to its immediately enclosing <Tabs>, and nested tab groups
// (e.g. build-tool tabs inside a language tab) are handled independently.
function parseGroups(text) {
const lines = text.split("\n");
const stack = [];
const groups = [];
lines.forEach((line, idx) => {
// A single line can, in principle, contain multiple tokens; scan in order.
// We process opens/items/closes by their column position on the line.
const tokens = [];
let m;
const openRe = /<(?:Tabs|ParamTabs)\b/g;
while ((m = openRe.exec(line))) tokens.push({ col: m.index, type: "open" });
const closeRe = /<\/(?:Tabs|ParamTabs)>/g;
while ((m = closeRe.exec(line)))
tokens.push({ col: m.index, type: "close" });
ITEM_RE.lastIndex = 0;
while ((m = ITEM_RE.exec(line))) {
tokens.push({ col: m.index, type: "item", label: m[1] ?? m[2] });
}
tokens.sort((a, b) => a.col - b.col);
for (const t of tokens) {
if (t.type === "open") {
const g = { line: idx + 1, labels: [] };
stack.push(g);
groups.push(g);
} else if (t.type === "close") {
stack.pop();
} else if (t.type === "item") {
if (stack.length) stack[stack.length - 1].labels.push(t.label);
}
}
});
return groups;
}

function checkGroup(labels) {
const langs = labels.filter(isLanguage).map(normalize);
for (let i = 1; i < langs.length; i++) {
if (RANK.get(langs[i]) < RANK.get(langs[i - 1])) {
return langs; // out of order
}
}
return null;
}

let violations = 0;
let filesWithViolations = 0;

for (const file of mdxFiles(DOCS_DIR)) {
const text = readFileSync(file, "utf8");
const groups = parseGroups(text);
let fileHadViolation = false;
for (const g of groups) {
const bad = checkGroup(g.labels);
if (bad) {
if (!fileHadViolation) {
console.error(`\n${relative(ROOT, file)}`);
fileHadViolation = true;
filesWithViolations++;
}
const expected = [...bad].sort((a, b) => RANK.get(a) - RANK.get(b));
console.error(
` line ${g.line}: [${bad.join(", ")}] → expected [${expected.join(", ")}]`,
);
violations++;
}
}
}

const canonicalStr = CANONICAL.join(" → ");
if (violations > 0) {
console.error(
`\n✖ ${violations} tab group(s) in ${filesWithViolations} file(s) violate the canonical language order (${canonicalStr}).`,
);
process.exit(1);
} else {
console.log(
`✓ All language tab groups follow the canonical order (${canonicalStr}).`,
);
}
62 changes: 31 additions & 31 deletions src/content/docs/commands/valkey-string.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,37 @@ Valkey strings store sequences of bytes, which may include text, serialized obje
Go strings are immutable sequences of bytes and can safely represent binary data.
</TabItem>

<TabItem label="PHP">
PHP uses native `string` type for all Valkey string operations. PHP strings are binary-safe and can contain arbitrary bytes.

#### Usage

Commands accept `string` arguments and return `string` or `mixed` values:

```php
// Create client
$client = new ValkeyGlide();
$client->connect(addresses: [['host' => 'localhost', 'port' => 6379]]);

// Using regular strings
$client->set('key', 'value');
$result = $client->get('key'); // Returns string: 'value'

// Using binary data - PHP strings are binary-safe
$binaryKey = "\x01\x02\x03\xFE";
$binaryValue = "\xDE\xAD\xBE\xEF";

$client->set($binaryKey, $binaryValue);
$result = $client->get($binaryKey); // Returns binary string
```

#### Type Handling

* PHP strings are binary-safe and can store any byte sequence
* No special type or conversion needed for binary data
* Commands return `string` for string values, `mixed` for commands that may return different types
</TabItem>

<TabItem label="C#">
C# uses `GlideString` as a wrapper type that can hold either a UTF-8 `string` or raw `byte[]` data.

Expand Down Expand Up @@ -260,35 +291,4 @@ Valkey strings store sequences of bytes, which may include text, serialized obje
byte[][] bytesArr = gsArr2.ToByteArrays(); // to byte[][]
```
</TabItem>

<TabItem label="PHP">
PHP uses native `string` type for all Valkey string operations. PHP strings are binary-safe and can contain arbitrary bytes.

#### Usage

Commands accept `string` arguments and return `string` or `mixed` values:

```php
// Create client
$client = new ValkeyGlide();
$client->connect(addresses: [['host' => 'localhost', 'port' => 6379]]);

// Using regular strings
$client->set('key', 'value');
$result = $client->get('key'); // Returns string: 'value'

// Using binary data - PHP strings are binary-safe
$binaryKey = "\x01\x02\x03\xFE";
$binaryValue = "\xDE\xAD\xBE\xEF";

$client->set($binaryKey, $binaryValue);
$result = $client->get($binaryKey); // Returns binary string
```

#### Type Handling

* PHP strings are binary-safe and can store any byte sequence
* No special type or conversion needed for binary data
* Commands return `string` for string values, `mixed` for commands that may return different types
</TabItem>
</Tabs>
14 changes: 7 additions & 7 deletions src/content/docs/concepts/architecture/async-execution.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,20 @@ To achieve this, each of GLIDE's clients supports the language's native asynchro
:::
</TabItem>

<TabItem label="Node">
```typescript
// Support Javascript Promises syntax
const status = await client.set("user:101", "active");
```
</TabItem>

<TabItem label="Java">
```java
// Async set operation using Future interface
CompletableFuture<String> future = client.set("user:101", "active");
```
</TabItem>

<TabItem label="Node">
```typescript
// Support Javascript Promises syntax
const status = await client.set("user:101", "active");
```
</TabItem>

<TabItem label="Go">
```go
go func(ctx){
Expand Down
60 changes: 30 additions & 30 deletions src/content/docs/concepts/architecture/memory-model.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ Under steady load, the Rust core also holds:
## Language-Specific Notes

<Tabs syncKey="progLangInExamples">
<TabItem label="Python">
The Rust core's native memory is visible in process RSS but **not** reported
by `sys.getsizeof` or the `tracemalloc` module — those inspect Python
objects only. To observe total footprint use OS-level tools (`ps`,
`/proc/self/status`, container memory metrics, or `psutil`).

Python response objects (`bytes`, `str`, lists for multi-value replies) live
on the CPython heap and are subject to normal reference-counted reclamation.
</TabItem>

<TabItem label="Java">
GLIDE's Java client does **not** use JVM NIO direct (off-heap) buffers for
network I/O. All socket reads and writes are performed in the Rust core;
Expand All @@ -79,16 +89,6 @@ Under steady load, the Rust core also holds:
point.
</TabItem>

<TabItem label="Python">
The Rust core's native memory is visible in process RSS but **not** reported
by `sys.getsizeof` or the `tracemalloc` module — those inspect Python
objects only. To observe total footprint use OS-level tools (`ps`,
`/proc/self/status`, container memory metrics, or `psutil`).

Python response objects (`bytes`, `str`, lists for multi-value replies) live
on the CPython heap and are subject to normal reference-counted reclamation.
</TabItem>

<TabItem label="Node">
The Rust core allocates outside the V8 heap; V8's `process.memoryUsage()`
reports `rss` (which includes the native side) and `heapUsed` (which does
Expand All @@ -104,12 +104,6 @@ Under steady load, the Rust core also holds:
based on OS-reported RSS, not `MemStats.Sys`.
</TabItem>

<TabItem label="C#">
The Rust core's allocations are outside the managed heap and will not be
reported by `GC.GetTotalMemory`. Process-level counters
(`Process.WorkingSet64`, OS-level RSS) reflect the true footprint.
</TabItem>

<TabItem label="PHP">
GLIDE PHP is a C extension built against the Zend Engine (PHP's runtime).
PHP objects — including associative arrays returned by commands — are
Expand All @@ -127,6 +121,12 @@ Under steady load, the Rust core also holds:
linked list on the C side. If the PHP process does not consume messages
promptly, this queue grows without limit.
</TabItem>

<TabItem label="C#">
The Rust core's allocations are outside the managed heap and will not be
reported by `GC.GetTotalMemory`. Process-level counters
(`Process.WorkingSet64`, OS-level RSS) reflect the true footprint.
</TabItem>
</Tabs>

## Tuning Knobs
Expand Down Expand Up @@ -172,6 +172,13 @@ expected.
**From the runtime:**

<Tabs syncKey="progLangInExamples">
<TabItem label="Python">
```python
import psutil, os
rss_mb = psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024
```
</TabItem>

<TabItem label="Java">
```java
// JVM heap
Expand All @@ -186,13 +193,6 @@ expected.
```
</TabItem>

<TabItem label="Python">
```python
import psutil, os
rss_mb = psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024
```
</TabItem>

<TabItem label="Node">
```javascript
const { rss, heapUsed, external } = process.memoryUsage();
Expand All @@ -207,13 +207,6 @@ expected.
```
</TabItem>

<TabItem label="C#">
```csharp
using System.Diagnostics;
long rss = Process.GetCurrentProcess().WorkingSet64;
```
</TabItem>

<TabItem label="PHP">
```php
// emalloc-tracked memory only (excludes Rust core)
Expand All @@ -224,6 +217,13 @@ expected.
$peakMemory = memory_get_peak_usage(true);
```
</TabItem>

<TabItem label="C#">
```csharp
using System.Diagnostics;
long rss = Process.GetCurrentProcess().WorkingSet64;
```
</TabItem>
</Tabs>

**From the OS:** on Linux, `cat /proc/$PID/status | grep VmRSS` gives the
Expand Down
Loading
Loading