reorganize the sample file directory

The filenames now represent composite ids (stream id + recording id) rather
than a separate uuid system with its own reservation for a few benefits:

  * This provides more information when there are inconsistencies.

  * This avoids the need for managing the reservations during recording. I
    expect this to simplify delaying flushing of newly written sample files.
    Now the directory has to be scanned at startup for files that never got
    written to the database, but that's acceptably fast even with millions of
    files.

  * Less information to keep in memory and in the recording_playback table.

I'd considered using one directory per stream, which might help if the
filesystem has trouble coping with huge directories. But that would mean each
dir has to be fsync()ed separately (more latency and/or more multithreading).
So I'll stick with this until I see concrete evidence of a problem that would
solve.

Test coverage of the error conditions is poor. I plan to do some restructuring
of the db/dir code, hopefully making steps toward testability along the way.
This commit is contained in:
Scott Lamb
2018-02-20 10:11:10 -08:00
parent e7f5733f29
commit 253f3de399
16 changed files with 618 additions and 429 deletions

View File

@@ -328,18 +328,20 @@ impl ServiceInner {
let mut prev = None;
let mut cur_off = 0;
db.list_recordings_by_id(stream_id, s.ids.clone(), |r| {
let recording_id = r.id.recording();
// Check for missing recordings.
match prev {
None if r.id == s.ids.start => {},
None if recording_id == s.ids.start => {},
None => return Err(Error::new(format!("no such recording {}/{}",
stream_id, s.ids.start))),
Some(id) if r.id != id + 1 => {
Some(id) if r.id.recording() != id + 1 => {
return Err(Error::new(format!("no such recording {}/{}",
stream_id, id + 1)));
},
_ => {},
};
prev = Some(r.id);
prev = Some(recording_id);
// Add a segment for the relevant part of the recording, if any.
let end_time = s.end_time.unwrap_or(i64::max_value());
@@ -348,11 +350,11 @@ impl ServiceInner {
let start = cmp::max(0, s.start_time - cur_off);
let end = cmp::min(d, end_time - cur_off);
let times = start as i32 .. end as i32;
debug!("...appending recording {}/{} with times {:?} \
(out of dur {})", r.stream_id, r.id, times, d);
debug!("...appending recording {} with times {:?} \
(out of dur {})", r.id, times, d);
builder.append(&db, r, start as i32 .. end as i32)?;
} else {
debug!("...skipping recording {}/{} dur {}", r.stream_id, r.id, d);
debug!("...skipping recording {} dur {}", r.id, d);
}
cur_off += d;
Ok(())