My app seems to cause Time Machine errors

I've written an PDF viewing app, and there seems to be a correlation between files opened by the app and files that Time Machine says couldn't be backed up.

The files can still "not be backed up", even after the app has closed them.

Is there anything I specifically need to do to sever the link between the file and the app?

How are you opening these files? Do they have attributes that are different than other files?

This is the only code I have to open the PDF:

   override func read(from data: Data, ofType typeName: String) throws {
        self.thePDFDocument = 
PDFDocument.init(data: data)
        if self.thePDFDocument == nil {
            throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
        }
    }

I set the PDFDocument to nil when I close it.

The code above is loading the PDFDocument from a Data object. It isn't doing any file system related operations. Can you show some source code where you're accessing the file system to get the Data object? How is your code interacting with these files?

I've written an PDF viewing app, and there seems to be a correlation between files opened by the app and files that Time Machine says couldn't be backed up.

Can you post the full contents of the error logging you're seeing? Also, are these files stored on your boot volume or somewhere else (particularly an HFS+ volume)?

It's possible there's an edge case I'm overlooking, but Time Machine’s general backup "flow" is to:

  1. Snapshot the source volume.
  2. Backup the contents of the snapshot.
  3. (eventually) delete the backup.

The major benefit of this is that it improves overall backup quality, as you're backing up a single "moment" in time, not a "spread" of time. However, it also means that your app’s own interactions with your files shouldn't really matter, as Time Machine isn't actually backing up the "same" file as the one you have open.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

My first thought was some kind of cross-platform PDF code that wasn't doing file coordination properly. But what you're showing is basic NSDocument file reading.

However, NSDocument can get very complicated when writing. You said you're writing a viewer app. Are you sure you've specified the role for PDF as "Viewer"?

There would have to be some other kind of change, possibly to attributes or permissions, in order for Time Machine to balk. Reading a file should be a no-op in that respect.

I'm just using openDocument, without overriding it.

Yes, the files are on the internal boot volume (APFS).

The error was flagged in the UI. TM reported that the backup hadn't completed, and in System Settings it reported the name of the file.

It has done this to various files: the only connection is they are all PDFs that are or have been open in my app. Closing the app and running TM fixes the error.

As usual, I can't find any relevant logs in Console.

Closing the app and running TM fixes the error.

So you're saying there is no error? Because that's how Time Machine works. It doesn't back up open files.

There is some uncertainty as to what constitutes an "open file" on macOS. Obviously an open SQLite file or a package being actively modified will qualify. But an NSDocument file, even though it opens a file only briefly, could replace the original at some later date. I'm happy to let the Apple folks debate those finer points. If the problem goes away when your app quits, then there is no problem.

You still haven't answered my question about your NSDocument Viewer/Editor role. Perhaps that is an improvement you can make.

I've been using TM since Leopard, and I have tens of documents regularly open in a variety of applications, and I've never seen this error until I started using my own app.

The app's role is an Editor, as it can modify and save files. Nonetheless, TextEdit, Pages, BBEdit, and countless other apps that I use don't have this problem.

I've been using TM since Leopard, and I have tens of documents regularly open in a variety of applications, and I've never seen this error until I started using my own app.

The app's role is an Editor, as it can modify and save files.

So, I think there are actually two different issues at work here:

  1. I think TM is delaying the backup because it's been told your document has pending, unsaved changes. I'd start by checking the "documentEdited" property, which should be "false" (unless the document has been edited).

  2. The fact that you need to quit the app (not just close the document) implies that you're leaking a document reference of some kind, which is then "extending" the impact of #1 beyond the point when you closed the document.

In terms of how you investigate this further, I would start with very focused testing to specifically validate the general "theory" above. A few ideas and suggestions:

  • What open files does Activity Monitor show when your app is running and is that matching up properly with what your app "should" be doing?

  • Checking the document volume properties while running to see how they change and if they match what you'd expect.

  • Focus on EXACTLY how the problem occurs. For example, if ALL you do is open and immediately close a PDF, is that enough to create the problem? If not, then what DOES it take to create the problem?

Finally, explaining this point:

Nonetheless, TextEdit, Pages, BBEdit, and countless other apps that I use don't have this problem.

The normal edit/save cycle is intended to be fast enough that apps don’t actually “hold” files open with unsaved changes. That lets TM backup files “in between” each of their saves. This is also where the “safe save” file exchange architecture is important— by writing out a new file and then swapping it with the original, TM can’t actually back up any intermediate state.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I set the PDFDocument to nil when I close it.

Maybe review how you're doing that. The NSDocument teardown process is not straightforward. As the documentation says, the "close()" method doesn't always get called.

This is a general problem throughout the UI APIs. The init, setup, layout, display, hide, teardown, dealloc process can be quite delicate. This is most apparent with NSDocument, where everything either happens at an inconvenient time, or simply not at all.

It's better to rely on major events like NSWindowDelegate "windowWillClose" to perform your teardown. Make sure your teardown logic can be safely called repeatedly, and then call it again in places like close() and dealloc.

The "Debug Memory Graph" tool is very useful. Just run your app normally. Open a couple of documents and close them. Then hit Debug Memory Graph. You may be surprised at what objects still exist at that point.

Might the document's size be an issue? They tend to be over 100 Mb. It's certainly not ALL the documents that I open: only a few.

It's worth saying that I've deliberately switched off the auto-save functionality (autosavesInPlace = false).

Can I ask what you mean by "checking the documentEdited property"..?

If I close a document with unsaved changes, it flags an alert saying that there are unsaved changes; do you want to save them. If there aren't, then the document closes gracefully.

I'll see if I can trigger it again and investigate a bit.

Thanks

Oh - one other possible factor: the files are Locked in the Finder, to prevent edits.

They've been on a disk for over a year, unchanged, as far as I know.

My app seems to cause Time Machine errors
 
 
Q