0xMiden / 0xMiden/note-transport-service

StreamNotes ignores the request `cursor`, so clients can't resume a stream

Abierto
#96 4 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Rust
Estrellas
3
Forks
10
Merge medio
2 h 23 min
PR fusionados (30 d)
4

Descripción

It looks like `StreamNotes` quietly drops the `cursor` field that clients send in `StreamNotesRequest`. The pull-based `FetchNotes` uses it, but the streaming path doesn't, so there's no way to resume a stream from a known point.

### Background

Both requests carry a `cursor`:

- `FetchNotesRequest { tags, cursor }`
- `StreamNotesRequest { tag, cursor }`

`FetchNotes` honors it — it reads `request_data.cursor` and passes it straight into the DB query, so a client can resume from wherever it left off ([mod.rs#L234](https://github.com/0xMiden/note-transport-service/blob/91ca4de34a8b88435e452dda0ed185b90f0bcda0/crates/node/src/node/grpc/mod.rs#L234)).

`StreamNotes` only logs the cursor and then ignores it ([mod.rs#L292](https://github.com/0xMiden/note-transport-service/blob/91ca4de34a8b88435e452dda0ed185b90f0bcda0/crates/node/src/node/grpc/mod.rs#L292)). `Subface::new(id, tag, tx)` has no cursor parameter ([streaming.rs#L255](https://github.com/0xMiden/note-transport-service/blob/91ca4de34a8b88435e452dda0ed185b90f0bcda0/crates/node/src/node/grpc/streaming.rs#L255)), so a subscription always starts from the server's shared per-tag cursor instead of where the client asked.

The server also hands a `cursor` back to the client in every `StreamNotesUpdate`, which only really makes sense if the client is meant to send it back to resume — but the request side ignores it, so that round-trip is broken.

### Steps to reproduce

1. Open a `StreamNotes` for some tag and read a few updates. Note the `cursor` the server returns.
2. Drop the connection (or simulate a reconnect).
3. Open `StreamNotes` again for the same tag with `cursor` set to the last value you saw.
4. Look at what you actually receive.

### What I expected

The stream picks up roughly from the cursor I passed — I get notes after that point, with no big replay and no gap.

### What actually happens

The requested cursor is ignored. Depending on whether anyone else is subscribed to that tag, one of two things happens:

- **Lone reconnect → full replay.** When the last subscriber for a tag disconnects, the whole `TagData` (and its cursor) gets removed ([streaming.rs#L178](https://github.com/0xMiden/note-transport-service/blob/91ca4de34a8b88435e452dda0ed185b90f0bcda0/crates/node/src/node/grpc/streaming.rs#L178)). So a single client that reconnects gets a fresh `TagData` with `cursor: 0` ([streaming.rs#L262](https://github.com/0xMiden/note-transport-service/blob/91ca4de34a8b88435e452dda0ed185b90f0bcda0/crates/node/src/node/grpc/streaming.rs#L262)) and re-receives the entire history for that tag, every reconnect. For something like a wallet on a flaky connection that's a repeated full re-download.
- **Late joiner → missed notes.** If another subscriber is keeping the tag alive at a higher shared cursor, a newly connecting client inherits that cursor and silently skips every note between the cursor it asked for and the current shared value — exactly the ones it was trying to catch up on.

As a side effect, the starting point is also inconsistent between subscribers: the first subscriber to a tag starts at 0 (full history), while a later subscriber to the same tag starts at "now" (nothing), even though they made the same request.

### Notes

The root cause is just that the request cursor never reaches the streamer. `stream_notes` would need to thread `request_data.cursor` through to the subscription so each sub starts from its own requested position, rather than relying on a single shared per-tag cursor. That likely also means tracking the cursor per subscriber instead of per tag, since different subscribers can legitimately be at different points.

This is closely related to #28 (client re-connects) — resuming via the request cursor is the mechanism that makes reconnects work without gaps or full replays.

I'd be glad to take this on — could I be assigned?

Guía de contribución

Abrir la guía de contribución

Línea de trabajo

The issue is in the gRPC streaming implementation. Start by examining `crates/node/src/node/grpc/mod.rs` around line 292 where the cursor is logged and ignored, and `crates/node/src/node/grpc/streaming.rs` where `Subface::new` is defined. The fix involves modifying `stream_notes` to pass the request cursor to the subscription logic, likely changing `Subface::new` to accept a cursor and tracking it per subscriber. Run the existing tests to ensure the pull-based `FetchNotes` still works correctly.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
rust
Área
api, backend
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Tranquilo
Claridad
Bien especificado
Aptitud para principiantes
65/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.