We saw a case where the recovery stats reported `recovered_bytes` greater than `length_in_bytes` for one file:

```
GET /_recovery?detailed

200 OK
...
{
  "name": "_buk.fdt",
  "length_in_bytes": 26891307173,
  "reused": false,
  "recovered_in_bytes": 35324928266,
  "recovered_from_snapshot_in_bytes": 8433621093
}
...
```

NB `35324928266 == 26891307173 + 8433621093`

This causes Elasticsearch to compute a negative value for the reserved space on the target node of this recovery, which prevents stats APIs from working. This appears to be caused by a network-level failure which triggers a retry without first cancelling an ongoing download of a different file with the same name from the snapshot repository.

---

### Workaround

Restart the target node to clear these stats.
server/src/main/java/org/elasticsearch/index/shard/IndexShard.java
* 1) Shard is post recovery and sends shard started to the master
                     * 2) Node gets disconnected and rejoins
                     * 3) Master assigns the shard back to the node
                     * 4) Master processes the shard started and starts the shard
                     * 5) The node process the cluster state where the shard is both started and primary term is incremented.
                     *
                     * We could fail the shard in that case, but this will cause it to be removed from the insync allocations list
                     * potentially preventing re-allocation.
                     */
                    assert newRouting.initializing() == false
                        : "a started primary shard should never update its term; "
                            + "shard "
                            + newRouting
                            + ", "
                            + "current term ["
                            + pendingPrimaryTerm
                            + "], "
                            + "new term ["
                            + newPrimaryTerm
                            + "]";
                    assert newPrimaryTerm > pendingPrimaryTerm
                        : "primary terms can only go up; current term [" + pendingPrimaryTerm + "], new term [" + newPrimaryTerm + "]";
                    /*
                     * Before this call returns, we are guaranteed that all future operations are delayed and so this happens before we
                     * increment the primary term. The latch is needed to ensure that we do not unblock operations before the primary
                     * term is incremented.
                     */
                    
                    boolean resyncStarted = primaryReplicaResyncInProgress.compareAndSet(false, true);
                    if (resyncStarted == false) {