301 Commits
Author SHA1 Message Date
cozis d38f3c24e8 Add proxy options --addr, --max-ops, --client-id 2026-03-28 16:34:28 +01:00
cozis 544a07c63b Fix ubsan warning 2026-03-28 16:12:31 +01:00
cozis 8f4d51d28c Add build.zig 2026-03-28 16:06:14 +01:00
cozis 4db0a934c5 Add cool cluster pic 2026-03-27 18:10:41 +01:00
cozis 3f5c71242a Improve README 2026-03-19 11:36:02 +01:00
cozis 9e1e74b401 Move Motivation paragraph to the top in README 2026-03-18 10:39:39 +01:00
cozis 7b90857901 Change README title 2026-03-18 10:32:48 +01:00
cozis ae626774f1 Replace README.md with docs.md 2026-03-18 10:31:18 +01:00
cozis fcd27f3567 Add docs.md 2026-03-18 10:30:42 +01:00
cozis 089191b3e4 Remove TODO.txt 2026-03-16 11:26:04 +01:00
cozis 89dfd1fbc5 Add LICENSE 2026-02-26 11:29:12 +01:00
cozis 3e353d0ae1 Add README.md 2026-02-26 11:22:05 +01:00
cozis d74ed80ca0 Remove stress test scripts 2026-02-26 10:35:21 +01:00
Claudeandcozis 143b45e340 Fix 5 bugs found via wrk stress testing the HTTP proxy
1. tcp_write infinite loop: When the output byte queue enters an error
   state (buffer full), tcp_write would loop forever because
   byte_queue_write_buf returns {NULL,0} without advancing. Break out
   of the loop when the write buffer is empty.

2. Proxy ignores async return values: toastyfs_async_get/put/delete
   can fail (return -1) but the proxy unconditionally set the operation
   to STARTED, causing it to wait forever for a result that never comes.
   Now checks return values and responds with 500 on failure.

3. Use-after-free on closed connections: When a client disconnects while
   its request is queued as PENDING, the HTTP_Conn is freed but the proxy
   still holds stale pointers. Added http_response_builder_is_valid() to
   detect and discard operations for dead connections.

4. Wrong HTTP status codes: The proxy returned 500 for all errors. Now
   returns 507 (Insufficient Storage) for FULL, 404 for NOT_FOUND, and
   503 (Service Unavailable) when the operation queue is full.

5. TCP connection leak (root cause of proxy hang): When a peer closes a
   connection, process_conn_events closes the socket and sets HUP, but
   tcp_conn_free_maybe immediately frees the TCP_Conn before
   tcp_next_event can deliver the HUP event. The HTTP server never learns
   about the disconnect, so HTTP_Conn entries accumulate until max_conns
   is reached and the proxy stops accepting new connections. Fixed by
   only calling tcp_conn_free_maybe when the CLOSED flag is already set
   (meaning the user called tcp_close), preserving the connection for
   HUP event delivery otherwise.

Also adds wrk-based stress test suite (stress-test.sh + lua scripts).

https://claude.ai/code/session_01EHoWWnVgyCFxah5WaNV4kS
2026-02-26 10:33:01 +01:00
Claude d56181503e Fix cluster not responding after restart
Two bugs prevented the cluster from working after a stop/start cycle:

1. tcp_listen_tcp() and tcp_listen_tls() had reuse_addr set to false,
   causing bind() to fail with EADDRINUSE when ports are in TIME_WAIT
   state after a restart. Set SO_REUSEADDR to true.

2. The vsr_boot_marker file (created in CWD on first boot) was never
   cleaned up on cluster stop. On restart, all nodes would enter
   STATUS_RECOVERY, but since RECOVERY messages are only processed by
   nodes in STATUS_NORMAL, the entire cluster would deadlock with no
   node able to recover. Remove the marker in cluster.sh stop.

https://claude.ai/code/session_01U72omHJrk95xFwm22Rkqgt
2026-02-26 01:02:21 +00:00
Claudeandcozis 7cd124e939 Add cluster management script for 3 server nodes and 1 HTTP proxy
Provides start/stop/status/restart/clean commands to manage a local
ToastyFS cluster. Nodes listen on 127.0.0.1:8001-8003 with the HTTP
proxy on 127.0.0.1:3000. Each node gets its own chunk storage directory
under cluster-data/. Logs and PID files are stored there as well.

https://claude.ai/code/session_01FAL4T35UGYqXexbaxvihRp
2026-02-26 01:19:36 +01:00
cozis 02da6b0170 Rewrite build scripts 2026-02-26 00:55:06 +01:00
cozis 0fbcbda4f7 Fix invalid write to static buffer in parse_addr_arg 2026-02-26 00:11:34 +01:00
Claudeandcozis 5ff6a1e073 Fix compilation warnings 2026-02-25 23:59:06 +01:00
cozis 7591ef6ee1 Cleanup of message.c/.h 2026-02-25 23:45:53 +01:00
cozis e38ad93da8 Cleanup of tcp.c/.h 2026-02-25 19:15:48 +01:00
cozisandClaude Opus 4.6 1b53abf633 Fix Windows networking bugs and connection metadata mismatch
- Use closesocket() instead of close() for sockets on Windows (lib/tcp.c)
- Call WSAStartup in tcp_init so outbound connections work (lib/tcp.c)
- Check WSAGetLastError() instead of errno for recv/send on Windows (lib/tcp.c)
- Use WSAEWOULDBLOCK instead of EINPROGRESS for non-blocking connect (lib/tcp.c)
- Add NULL guard in tcp_write for stale connection handles (lib/tcp.c)
- Fix ConnMetadata/TCP_Conn index mismatch in get_next_message: use
  event.handle.idx directly instead of searching for a free metadata
  slot, which could diverge when stale entries remain from failed
  outbound connections (lib/message.c)
- Initialize num_senders to 0 and skip unused metadata in
  find_conn_by_target (lib/message.c)
- Clear ready flag in http_server_next_request to prevent infinite
  loop (lib/http_server.c)
- Add CRT invalid parameter handler for the HTTP proxy (src/main.c)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:10:01 +01:00
cozisandClaude Opus 4.6 6b4f773052 Implement file_truncate for Windows and add missing Quakey mocks
file_truncate had a stub `return -1` on Windows, causing all
chunk_store_write calls to fail (every PUT returned TRANSFER_FAILED).
Implement it using SetFilePointer + SetEndOfFile.

Add mock_SetEndOfFile and mock_GetFileAttributesA to Quakey so the
simulation can exercise the Windows code paths against the mock
filesystem. Without these, SetEndOfFile was unmocked (link error) and
GetFileAttributesA called the real Win32 API on mock paths, making
chunk_store_exists always return false.

Verified: seeds 1-100 pass with 2000s simulation time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 13:33:18 +01:00
cozis db2ef299bf Fix chunk replication 2026-02-25 13:13:46 +01:00
cozis 3da0eded42 First version of the HTTP proxy 2026-02-25 12:30:20 +01:00
cozis ef1d65ad2b Major refactoring 2026-02-25 10:44:43 +01:00
cozis bac3477991 Add comment 2026-02-24 23:16:46 +01:00
cozis 487f3bf010 Add disclaimer to README.md 2026-02-22 14:18:57 +01:00
cozis 11a0d9630e Add first version of the README.md 2026-02-22 14:17:54 +01:00
cozis 2c7bc5cd4e Simplify message routing in server 2026-02-22 13:46:23 +01:00
cozis 743528b92f Add TODO.txt 2026-02-22 13:46:04 +01:00
cozis b0cffd8198 Make client ID random in example 2026-02-22 00:41:01 +01:00
Claude 7da254fc45 Fix request_id mismatch causing 5s hang on every PUT commit
The COMMIT_PUT message was built with request_id N, then request_id
was incremented to N+1 immediately after sending. When the server
replied with request_id N, the client compared it against N+1,
silently discarded the reply, and waited for the 5-second timeout.
The timeout retry (replay_request) then resent with N+1, which
matched — so every PUT paid an unnecessary 5-second penalty.

Move the request_id++ before building the CommitPutMessage, matching
the pattern already used by toastyfs_async_delete.

https://claude.ai/code/session_01EG3WZSH3ZeTdAbb5T42r2w
2026-02-21 23:36:18 +00:00
Claude 15a2713116 Fix client hanging on first operation by queuing message during connect
When no TCP connection exists, send_message_to_server() called
tcp_connect() with a NULL output parameter and returned without
sending the message. The message was silently dropped, and the only
recovery was the 5-second PRIMARY_DEATH_TIMEOUT_SEC retry — causing
the client to hang on every cold-connection operation.

tcp_connect() already supports returning the output ByteQueue so
callers can queue data to be flushed once the connection completes.
Use that mechanism: get the output queue from tcp_connect() and
write the message into it immediately, so it is sent as soon as
the TCP handshake finishes.

https://claude.ai/code/session_01EG3WZSH3ZeTdAbb5T42r2w
2026-02-21 23:15:18 +00:00
cozis 9849e8790a Forward-declare pollfd in toastyfs.h 2026-02-22 00:08:37 +01:00
cozis 964a029154 Add parallel transfer limit to client 2026-02-22 00:07:53 +01:00
cozis 32c054a421 Only set SO_REUSEADDR when not in the simulation 2026-02-21 23:36:54 +01:00
Claude 510007d560 Add build artifacts to .gitignore
Ignore example binaries compiled to root directory and the
vsr_boot_marker runtime data file.

https://claude.ai/code/session_01JniNxxDP4NbsDXNGmGNG7H
2026-02-21 22:32:51 +00:00
Claude b0cc529773 Fix GET request returning corrupted data for multi-chunk blobs
Four bugs caused data mismatches between PUT and GET operations:

1. chunk_store_exists() used file_open (O_CREAT) to check if a chunk
   file exists, which created empty files as a side effect. Servers
   would then read from these empty files and return uninitialized
   heap memory as chunk data. Fixed by using file_exists (access())
   instead.

2. Server's process_fetch_chunk checked `ret < 0` after
   chunk_store_read, missing the EOF case (ret == 0) from empty
   files. Changed to `ret <= 0`.

3. begin_transfers() == 0 was used as a completion check in both
   STEP_FETCH_CHUNK and STEP_STORE_CHUNK, but returning 0 only means
   "no new transfers started", not "all done". With concurrent chunk
   transfers, this caused premature completion, leaving in-flight
   transfers to hit UNREACHABLE. Fixed by using
   all_chunk_transfers_completed() instead.

4. choose_store_locations_for_chunk() returned [0, 1] for ALL chunks
   regardless of index, while GET fetches chunk i from servers
   (i+j) % num_servers. This made some chunks unreachable. Fixed by
   using (chunk_idx + j) % num_servers for store locations.

Also added SO_REUSEADDR to the server listen socket so restarting
the cluster doesn't fail with "Couldn't setup TCP listener", and
added a note on mock_access about the simulation mismatch.

https://claude.ai/code/session_01JniNxxDP4NbsDXNGmGNG7H
2026-02-21 22:31:33 +00:00
cozis fa3dc2483b Use single-line comments in example 2026-02-21 22:13:18 +01:00
cozis 0c044d8dba Add README.md 2026-02-21 16:10:40 +01:00
cozis 2a3bbf8837 Remove unused docker stuff 2026-02-21 16:02:14 +01:00
cozis a6bdd94cbf Merge branch 'claude/library-build-container-Dd9Tg' of https://github.com/cozis/TinyDFS into claude/library-build-container-Dd9Tg 2026-02-21 16:00:28 +01:00
Claude 96bd81bae6 Add library build, local cluster script, and example client
Introduce a Makefile that builds libtoastyfs.a and libtoastyfs.so from the
client-side sources (client, tcp, byte_queue, message, basic) so external
programs can link against the ToastyFS client API in include/toastyfs.h.

Add cluster.sh, a convenience script that manages a local 3-node cluster
by spawning toastyfs server processes on ports 8081-8083.  Sub-commands:
up, down, status, logs, and "run <source.c>" which builds the library,
compiles the source against it, starts the cluster, runs the binary, and
tears everything down.

Add examples/example.c demonstrating synchronous PUT, GET, DELETE, and a
NOT_FOUND verification.

Fix a stack overflow in the MAIN_SERVER entry point: ServerState is ~40 MB
(MetaStore holds 4096 ObjectMeta entries) and was declared on the stack.
Heap-allocate it instead.

https://claude.ai/code/session_019DVhmc25jfzcHnmdNxzib2
2026-02-21 14:51:14 +00:00
Claude a57e713fda Add library build, Docker cluster, and example client
Introduce a Makefile that builds libtoastyfs.a and libtoastyfs.so from the
client-side sources (client, tcp, byte_queue, message, basic) so external
programs can link against the ToastyFS client API in include/toastyfs.h.

Add a Dockerfile and docker-compose.yml that spin up a 3-node ToastyFS
server cluster on a private bridge network with ports 8081-8083 exposed
on the host. Include cluster.sh as a convenience wrapper (up/down/status/
logs/run) — the "run" sub-command builds the library, compiles a user-
supplied C source against it, starts the cluster, and executes the binary.

Add examples/example.c demonstrating synchronous PUT, GET, DELETE, and a
NOT_FOUND verification.

https://claude.ai/code/session_019DVhmc25jfzcHnmdNxzib2
2026-02-21 14:30:58 +00:00
Claude fe7d79098e Move CEIL macro to basic.h and remove unnecessary heartbeat reset
- Move the CEIL macro from config.h to basic.h alongside MIN/MAX, and
  use it in deadline_to_timeout for the rounding-up division
- Remove the heartbeat reset in complete_view_change_and_become_primary
  since it was not needed — heartbeat is already set when the node
  enters STATUS_CHANGE_VIEW, which is close enough in time

https://claude.ai/code/session_0184gHjra7fsmSPZ4kppaGhC
2026-02-21 14:18:30 +00:00
Claude 8233392871 Fix client timeout propagation and add operation retry logic
The client library had several issues preventing operations from completing
in the simulation:

- toastyfs_register_events discarded the computed timeout instead of
  returning it to callers, so clients never woke up on schedule
- random_client_tick ignored the timeout parameter entirely
- deadline_to_timeout truncated sub-millisecond values to 0 instead of
  rounding up, causing busy-wait loops
- The server didn't reset the heartbeat timer after completing a view
  change, so followers would immediately timeout again
- The client's DELETE handler didn't handle META_RESULT_NOT_FOUND,
  causing an assertion failure when deleting non-existent keys
- Most critically, messages sent before TCP connections were established
  were silently dropped with no retry mechanism — added timeout-based
  retry logic in toastyfs_process_events

https://claude.ai/code/session_0184gHjra7fsmSPZ4kppaGhC
2026-02-21 14:08:07 +00:00
Claude 6e60b41f1b Add client trace lines and fix random client first-operation bug
Add logging infrastructure to client.c mirroring the server's node_log
pattern, with trace lines for: init, async put/get/delete, begin
transfers, replay requests, all received message types (store ack, fetch
chunk response, redirect, reply, get blob response), disconnects, and
unexpected messages.

Add result/operation trace lines to random_client.c and fix a bug where
the random client never started its first operation (the condition
`result.type != TOASTYFS_RESULT_VOID` was always false on the first
tick since no operation was pending).

https://claude.ai/code/session_0184gHjra7fsmSPZ4kppaGhC
2026-02-21 13:41:22 +00:00
Claude ab2ee3fe3b Add review feedback: SHA256 warning, MAX_TRANSFERS macro, CEIL macro, and TODO notes
- Add #warning to remind about replacing compute_chunk_hash with proper SHA256
- Define MAX_TRANSFERS and CEIL macros in config.h and use them
- Add TODO noting server selection formula is temporary
- Add NOTE about sticky error pattern in async functions
- Add TODO for INVALID_TIME error handling at all get_current_time call sites
- Add TODO about determining timeout based on pending operation status

https://claude.ai/code/session_01TkNavR4qyZcTuXMPmrtWUJ
2026-02-21 13:18:49 +00:00
Claude 723fc7cf76 Complete client library refactoring: fill all placeholders and implement missing functions
- Add REPLICATION_FACTOR and CHUNK_SIZE constants to config.h
- Add proper error codes (REJECTED, FULL, NOT_FOUND, TRANSFER_FAILED, etc.) to toastyfs.h
- Implement compute_chunk_hash() for deterministic chunk hashing
- Implement replay_request() to resend operations after leader redirect
- Implement send_message_to_server_ex() for sending messages with trailing data
- Implement choose_store_locations_for_chunk() to pick REPLICATION_FACTOR servers
- Fix begin_transfers() to handle both StoreChunk (PUT) and FetchChunk (GET)
- Complete toastyfs_async_put(): chunk splitting, hashing, data copy, metadata setup
- Complete toastyfs_async_get()/delete(): key setup, step/time initialization
- Fix get_result(): use actual transfer sizes instead of fixed chunk_size, free transfer data
- Replace all xxx placeholders and TOASTYFS_ERROR_XXX with proper values
- Add chunk_sizes[] tracking and put_data lifetime management
- Implement random_client.c: rng(), choose_random_oper(), proper API call arguments

https://claude.ai/code/session_01TkNavR4qyZcTuXMPmrtWUJ
2026-02-21 13:03:13 +00:00