Skip to content
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
bb346ee
Redis
Jarred-Sumner Apr 4, 2025
1df710e
Tests
Jarred-Sumner Apr 4, 2025
d5b88cd
cool
Jarred-Sumner Apr 4, 2025
5b5f081
Merge branch 'main' into jarred/redis2
Jarred-Sumner Apr 5, 2025
a751174
Bunch of fixes
Jarred-Sumner Apr 5, 2025
ad7254c
updat
Jarred-Sumner Apr 6, 2025
58b38f6
Update valkey.d.ts
Jarred-Sumner Apr 6, 2025
0bf7889
various
Jarred-Sumner Apr 6, 2025
2f184b5
more
Jarred-Sumner Apr 6, 2025
ca61726
more
Jarred-Sumner Apr 6, 2025
773eea5
Make this work better
Jarred-Sumner Apr 6, 2025
741f133
Implement TLS & unix domain socket support
Jarred-Sumner Apr 6, 2025
d0f6d60
wip
Jarred-Sumner Apr 6, 2025
1b06b9a
valkey
Jarred-Sumner Apr 6, 2025
6d36561
rename a bunch of files
Jarred-Sumner Apr 6, 2025
072f3dc
ok
Jarred-Sumner Apr 6, 2025
e2a5e10
prepare
Jarred-Sumner Apr 6, 2025
04de976
ok
Jarred-Sumner Apr 6, 2025
102ed3d
Rename
Jarred-Sumner Apr 6, 2025
b3b2c9a
more
Jarred-Sumner Apr 6, 2025
36b1b68
more
Jarred-Sumner Apr 6, 2025
3158801
asd
Jarred-Sumner Apr 6, 2025
b0a7a0c
Update complex-operations.test.ts
Jarred-Sumner Apr 6, 2025
cae5e00
Rename sendCommand to send
Jarred-Sumner Apr 6, 2025
193a9c3
Move some things around
Jarred-Sumner Apr 6, 2025
2b15458
Prepare for auto pipelining
Jarred-Sumner Apr 6, 2025
04ad0bf
Update tests
Jarred-Sumner Apr 6, 2025
d94c01d
auto pipelining
Jarred-Sumner Apr 6, 2025
45b0aa3
Handle finalization better
Jarred-Sumner Apr 6, 2025
b39d2c0
Move offset byte list
Jarred-Sumner Apr 6, 2025
bb5922c
Fix fragmented message case
Jarred-Sumner Apr 6, 2025
bbf2a27
Update redis.md
Jarred-Sumner Apr 6, 2025
6f4b64d
Detach the socket properly
Jarred-Sumner Apr 6, 2025
ce13907
Update test-utils.ts
Jarred-Sumner Apr 6, 2025
587c8a5
Update valkey.zig
Jarred-Sumner Apr 6, 2025
85cd182
fix?
Jarred-Sumner Apr 7, 2025
5204e80
make auto pipelining configurable
Jarred-Sumner Apr 7, 2025
54807c4
Update valkey.zig
Jarred-Sumner Apr 7, 2025
7f3b770
Fix auto pipelining
Jarred-Sumner Apr 7, 2025
86ed048
more
Jarred-Sumner Apr 7, 2025
b6b3fcf
Clean up timeout logic
Jarred-Sumner Apr 7, 2025
e0f91d3
wip
Jarred-Sumner Apr 8, 2025
b901455
Merge branch 'main' into jarred/redis2
Jarred-Sumner Apr 8, 2025
5eae0fa
Get the tests to run in CI
Jarred-Sumner Apr 8, 2025
e9a1de5
Update ban-words.test.ts
Jarred-Sumner Apr 8, 2025
82843b7
Fixups
Jarred-Sumner Apr 8, 2025
6b2baec
Update test-utils.ts
Jarred-Sumner Apr 8, 2025
dc70fd6
Add more methods, fix test
Jarred-Sumner Apr 8, 2025
3103b9e
Update test-utils.ts
Jarred-Sumner Apr 8, 2025
5b8de0e
Avoid unix domain sockets for now
Jarred-Sumner Apr 8, 2025
e70e918
Update valkey.classes.ts
Jarred-Sumner Apr 8, 2025
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
503 changes: 503 additions & 0 deletions docs/api/redis.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/nav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ export default {
page("api/file-io", "File I/O", {
description: `Read and write files fast with Bun's heavily optimized file system API.`,
}), // "`Bun.write`"),
page("api/redis", "Redis client", {
description: `Bun provides a fast, native Redis client with automatic command pipelining for better performance.`,
}),
page("api/import-meta", "import.meta", {
description: `Module-scoped metadata and utilities`,
}), // "`bun:sqlite`"),
Expand Down
2 changes: 1 addition & 1 deletion packages/bun-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/// <reference path="./wasm.d.ts" />
/// <reference path="./overrides.d.ts" />
/// <reference path="./deprecated.d.ts" />

/// <reference path="./redis.d.ts" />
/// <reference path="./bun.ns.d.ts" />

// @ts-ignore Must disable this so it doesn't conflict with the DOM onmessage type, but still
Expand Down
264 changes: 264 additions & 0 deletions packages/bun-types/redis.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
declare module "bun" {
export interface RedisOptions {
/**
* URL to connect to, defaults to "redis://localhost:6379"
* Supported protocols: redis://, rediss://, redis+unix://, redis+tls://
*/
url?: string;

/**
* Connection timeout in milliseconds
* @default 10000
*/
connectionTimeout?: number;

/**
* Socket timeout in milliseconds
* @default 0 (no timeout)
*/
socketTimeout?: number;

/**
* Idle timeout in milliseconds
* @default 0 (no timeout)
*/
idleTimeout?: number;

/**
* Whether to automatically reconnect
* @default true
*/
autoReconnect?: boolean;

/**
* Maximum number of reconnection attempts
* @default 10
*/
maxRetries?: number;

/**
* Whether to queue commands when disconnected
* @default true
*/
enableOfflineQueue?: boolean;

/**
* TLS options
* Can be a boolean or an object with TLS options
*/
tls?:
| boolean
| {
key?: string | Buffer;
cert?: string | Buffer;
ca?: string | Buffer | Array<string | Buffer>;
rejectUnauthorized?: boolean;
};
}

export class RedisClient {
/**
* Creates a new Redis client
* @param url URL to connect to, defaults to process.env.VALKEY_URL, process.env.REDIS_URL, or "valkey://localhost:6379"
* @param options Additional options
*
* @example
* ```ts
* const valkey = new RedisClient();
*
* await valkey.set("hello", "world");
*
* console.log(await valkey.get("hello"));
* ```
*/
constructor(url?: string, options?: RedisOptions);

/**
* Whether the client is connected to the Redis server
*/
readonly connected: boolean;

/**
* Amount of data buffered in bytes
*/
readonly bufferedAmount: number;

/**
* Callback fired when the client connects to the Redis server
*/
onconnect: ((this: RedisClient) => void) | null;

/**
* Callback fired when the client disconnects from the Redis server
* @param error The error that caused the disconnection
*/
onclose: ((this: RedisClient, error: Error) => void) | null;

/**
* Connect to the Redis server
* @returns A promise that resolves when connected
*/
connect(): Promise<void>;

/**
* Disconnect from the Redis server
*/
disconnect(): void;

/**
* Send a raw command to the Redis server
* @param command The command to send
* @param args The arguments to the command
* @returns A promise that resolves with the command result
*/
send(command: string, args: string[]): Promise<any>;

/**
* Get the value of a key
* @param key The key to get
* @returns Promise that resolves with the key's value, or null if the key doesn't exist
*/
get(key: string | NodeJS.TypedArray | Blob): Promise<string | null>;

/**
* Set the value of a key
* @param key The key to set
* @param value The value to set
* @returns Promise that resolves with "OK" on success
*/
set(key: string | NodeJS.TypedArray | Blob, value: string | NodeJS.TypedArray | Blob): Promise<"OK">;

/**
* Delete a key
* @param key The key to delete
* @returns Promise that resolves with the number of keys removed
*/
del(key: string | NodeJS.TypedArray | Blob): Promise<number>;

/**
* Increment the integer value of a key by one
* @param key The key to increment
* @returns Promise that resolves with the new value
*/
incr(key: string | NodeJS.TypedArray | Blob): Promise<number>;

/**
* Decrement the integer value of a key by one
* @param key The key to decrement
* @returns Promise that resolves with the new value
*/
decr(key: string | NodeJS.TypedArray | Blob): Promise<number>;

/**
* Determine if a key exists
* @param key The key to check
* @returns Promise that resolves with true if the key exists, false otherwise
*/
exists(key: string | NodeJS.TypedArray | Blob): Promise<boolean>;

/**
* Set a key's time to live in seconds
* @param key The key to set the expiration for
* @param seconds The number of seconds until expiration
* @returns Promise that resolves with 1 if the timeout was set, 0 if not
*/
expire(key: string | NodeJS.TypedArray | Blob, seconds: number): Promise<number>;

/**
* Get the time to live for a key in seconds
* @param key The key to get the TTL for
* @returns Promise that resolves with the TTL, -1 if no expiry, or -2 if key doesn't exist
*/
ttl(key: string | NodeJS.TypedArray | Blob): Promise<number>;

/**
* Set multiple hash fields to multiple values
* @param key The hash key
* @param fieldValues An array of alternating field names and values
* @returns Promise that resolves with "OK" on success
*/
hmset(key: string | NodeJS.TypedArray | Blob, fieldValues: string[]): Promise<"OK">;

/**
* Get the values of all the given hash fields
* @param key The hash key
* @param fields The fields to get
* @returns Promise that resolves with an array of values
*/
hmget(key: string | NodeJS.TypedArray | Blob, fields: string[]): Promise<Array<string | null>>;

/**
* Check if a value is a member of a set
* @param key The set key
* @param member The member to check
* @returns Promise that resolves with true if the member exists, false otherwise
*/
sismember(key: string | NodeJS.TypedArray | Blob, member: string): Promise<boolean>;

/**
* Add a member to a set
* @param key The set key
* @param member The member to add
* @returns Promise that resolves with 1 if the member was added, 0 if it already existed
*/
sadd(key: string | NodeJS.TypedArray | Blob, member: string): Promise<number>;

/**
* Remove a member from a set
* @param key The set key
* @param member The member to remove
* @returns Promise that resolves with 1 if the member was removed, 0 if it didn't exist
*/
srem(key: string | NodeJS.TypedArray | Blob, member: string): Promise<number>;

/**
* Get all the members in a set
* @param key The set key
* @returns Promise that resolves with an array of all members
*/
smembers(key: string | NodeJS.TypedArray | Blob): Promise<string[]>;

/**
* Get a random member from a set
* @param key The set key
* @returns Promise that resolves with a random member, or null if the set is empty
*/
srandmember(key: string | NodeJS.TypedArray | Blob): Promise<string | null>;

/**
* Remove and return a random member from a set
* @param key The set key
* @returns Promise that resolves with the removed member, or null if the set is empty
*/
spop(key: string | NodeJS.TypedArray | Blob): Promise<string | null>;

/**
* Increment the integer value of a hash field by the given number
* @param key The hash key
* @param field The field to increment
* @param increment The amount to increment by
* @returns Promise that resolves with the new value
*/
hincrby(key: string | NodeJS.TypedArray | Blob, field: string, increment: string | number): Promise<number>;

/**
* Increment the float value of a hash field by the given amount
* @param key The hash key
* @param field The field to increment
* @param increment The amount to increment by
* @returns Promise that resolves with the new value as a string
*/
hincrbyfloat(key: string | NodeJS.TypedArray | Blob, field: string, increment: string | number): Promise<string>;
}

/**
* Default Redis client
*
* Connection information populated from one of, in order of preference:
* - `process.env.VALKEY_URL`
* - `process.env.REDIS_URL`
* - `"valkey://localhost:6379"`
*
*/
export const redis: RedisClient;
}
1 change: 1 addition & 0 deletions src/analytics/analytics_thread.zig
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ pub const Features = struct {
pub var process_dlopen: usize = 0;
pub var postgres_connections: usize = 0;
pub var s3: usize = 0;
pub var valkey: usize = 0;
pub var csrf_verify: usize = 0;
pub var csrf_generate: usize = 0;
pub var unsupported_uv_function: usize = 0;
Expand Down
51 changes: 51 additions & 0 deletions src/baby_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -408,3 +408,54 @@ pub fn BabyList(comptime Type: type) type {
}
};
}

pub fn OffsetList(comptime Type: type) type {
return struct {
head: u32 = 0,
byte_list: List = .{},

const List = BabyList(Type);
const ThisList = @This();

pub fn init(head: u32, byte_list: List) ThisList {
return .{
.head = head,
.byte_list = byte_list,
};
}

pub fn write(self: *ThisList, allocator: std.mem.Allocator, bytes: []const u8) !void {
_ = try self.byte_list.write(allocator, bytes);
}

pub fn slice(this: *ThisList) []u8 {
return this.byte_list.slice()[0..this.head];
}

pub fn remaining(this: *ThisList) []u8 {
return this.byte_list.slice()[this.head..];
}

pub fn consume(self: *ThisList, bytes: u32) void {
self.head +|= bytes;
if (self.head >= self.byte_list.len) {
self.head = 0;
self.byte_list.len = 0;
}
}

pub fn len(self: *const ThisList) u32 {
return self.byte_list.len - self.head;
}

pub fn clear(self: *ThisList) void {
self.head = 0;
self.byte_list.len = 0;
}

pub fn deinit(self: *ThisList, allocator: std.mem.Allocator) void {
self.byte_list.deinitWithAllocator(allocator);
self.* = .{};
}
};
}
Loading