Original text:
If a sender runs out of flow control credit, it will be unable to send new data and is considered blocked.  A sender SHOULD send a STREAM_DATA_BLOCKED or DATA_BLOCKED frame to indicate it has data to <del> write but is blocked by flow control limits.  These frames are expected to be sent infrequently in common cases, but they are considered useful for debugging and monitoring purposes. A sender SHOULD NOT send multiple STREAM_DATA_BLOCKED or DATA_BLOCKED frames for the same data limit, unless the original frame is determined to be lost.  Another STREAM_DATA_BLOCKED or DATA_BLOCKED frame can be sent after the data limit is increased. </del>  4.2. when sending an ack-eliciting packet (see QUIC-RECOVERY), but only if no other ack-eliciting packets have been sent since last receiving a packet.  Restarting when sending packets ensures that connections do <del> not prematurely time out when initiating new activity. </del>  The value for an idle timeout can be asymmetric.  The value advertised by an endpoint is only used to determine whether the

Reviewer comments:
To prevent a connection from going idle while it has application data to send, this PR recommends periodically sending the blocked frame as a keep alive.
Hmm, this is a design change.
NAME I'd argue this whole section should have similar text for STREAMS_BLOCKED, not just the text this PR is adding.
An issue to correspond to PR NAME and I believe this is likely a design issue.
Oops, this was unnecessary. Sorry for the churn.
If a connection gets send blocked for long enough (i.e. the peer application isn't draining the data right now, and thus not giving more flow control credit) the idle timer will fire. This is because no data is being sent over the wire while the sender is blocked. To me, it seems that we shouldn't consider a connection as idle if the application is actively trying to send. While in this blocked state, should the transport periodically send some ACK eliciting packet to keep the connection alive?
It is not much different from being blocked because there is currently nothing to send. The timeout will clean up connections that do not make progress. If the application is happy with waiting a very long time to proceed, it could send pings, but it might as well want to call it a day. So I'm not sure the spec needs to say anything about that. We are talking about 10 minutes of no progress by default.
It seems reasonable to use a keep-alive. If you have something you want to use the connection for and you aren't able to maintain activity on the connection, then either poke it or - more likely - give up. I don't think that we need a design change for this, but if you want to write some text, I wouldn't be opposed to taking a look.
Thinking about this, another solution would be to continue to send a *blocked frame, while in the blocked state. Even if the peer acknowledged the first blocked frame, if you're still blocked, you send it again. Personally, for my implementation, this would be the easiest thing to implement. I don't think the spec should make any requirements in this area, but I do think it should call this problem out and possibly make a recommendation.
Sending a BLOCKED frame repeatedly, maybe on exponential backoff, makes sense to me. NAME any interest in writing a PR for this one?
I could definitely write a PR. We implement exactly this. I would argue though that it's not exponential. It's immediate and will go forever. The app has data to send. The connection is not idle, and should never idle out in this scenario. It's the app's responsibility to time out, if their protocol dictates as such (which it might not, if they expect to be blocked while a long running operation is processed).
This should be a design issue. The proposal to send multiple BLOCKED frames is a design change.
NAME Corresponding text already exists in section 4.5. My point is that we might want to update this too, as the sender should behave the same way when it is blocked by data flow control vs. when blocked by stream-level concurrency.Still LGTM

Revised text:
If a sender runs out of flow control credit, it will be unable to send new data and is considered blocked.  A sender SHOULD send a STREAM_DATA_BLOCKED or DATA_BLOCKED frame to indicate it has data to  <ins> write but is blocked by flow control limits.  If a sender is blocked for a period longer than the idle timeout (idle-timeout), the connection might be closed even when data is available for transmission.  To keep the connection from closing, a sender that is flow control limited SHOULD periodically send a STREAM_DATA_BLOCKED or DATA_BLOCKED frame when it has no ack-eliciting packets in flight. </ins> 4.2. when sending an ack-eliciting packet (see QUIC-RECOVERY), but only if no other ack-eliciting packets have been sent since last receiving a packet.  Restarting when sending packets ensures that connections do  <ins> not prematurely time out when initiating new activity.  An endpoint might need to send packets to avoid an idle timeout if it is unable to send application data due to being blocked on flow control limits; see flow-control. </ins> The value for an idle timeout can be asymmetric.  The value advertised by an endpoint is only used to determine whether the