stephencelis / SQLite.swift

A type-safe, Swift-language layer over SQLite3.
MIT License
9.6k stars 1.55k forks source link

Thread Safe? #259

Open shnhrrsn opened 8 years ago

shnhrrsn commented 8 years ago

Apologies if I missed it, but I looked through the docs and I haven't seen anything mention whether or not SQLite.swift is thread safe.

I'm seeing some random EXC_BAD_ACCESS crashing that pops up inside of SQLite.swift when calling the libsqlite functions and I'm trying to determine if there's a leak or if it's related to using multiple threads to access the DB potentially concurrently.

stephencelis commented 8 years ago

There's a "Note on Thread Safety" here:

https://github.com/stephencelis/SQLite.swift/blob/c9d3082d1f5ddc2e55a792a8eb1a677b8a1b15f5/Documentation/Index.md#a-note-on-thread-safety

Do you have more information related to the crash you're seeing?

torstenlehmann commented 8 years ago

I'm also interested in this. What exactly does "Every database" mean? Every Connection?

shnhrrsn commented 8 years ago

@stephencelis Next time one pops up I'll forward it along, but from memory EXC_BAD_ACCESS is triggering in Statement.columnNames and Statement.step() when calling the respective libsqlite functions.

All crashes have come from operations submitted to an NSOperationQueue where there's a bunch of simultaneous reading and write transactions. This is part of an overall sync between the local DB and a sizable remote data source that triggers ~hourly, so it's fairly intensive on the DB and I wouldn't be surprised if I'm tripping some edge cases here.

stephencelis commented 8 years ago

I'm also interested in this. What exactly does "Every database" mean? Every Connection?

Yep, I'll update the docs to be clearer.

Next time one pops up I'll forward it along, but from memory EXC_BAD_ACCESS is triggering in Statement.columnNames and Statement.step() when calling the respective libsqlite functions.

The step function is dispatched to a queue, but columnNames is not. I hadn't thought that would be a problem, but if you internally use the sync helper (on Connection), I wonder if the problem will go away.

shnhrrsn commented 8 years ago

Just ran into this again, but this time it was in filter:

malloc: *** error for object 0x7f7ff7ace1f8: incorrect checksum for freed object - object was probably modified after being freed. *** set a breakpoint in malloc_error_break to debug

Specifically the stack trace is pointing to this code in Expression.swift:

public init<U : ExpressionType>(_ expression: U) {
   self.init(expression.template, expression.bindings)
}

The code on my end I'm using to make the call looks approximately like this:

if let id = object.someId, dateString = object.someDate {
    self.operationQueue.addOperationWithBlock {
        if let row = db.pluck(table.filter(idField == id).filter(dateField == dateString)) {
            // ...
        }
    }
}

If this were Objective-C, I'd say this was probably my fault, I missed a retain somewhere, but if let in Swift should handle that.

I'm still not close to ruling out it's something I'm doing, but it does seem to be related to threading.

mikemee commented 8 years ago

@shnhrrsn Note that PR #290 adds more error trapping, though still not for the pluck method (being discussed in #305).

If you replace db.pluck with db.prepare and a limit 1 restriction, you'll essentially have the same thing and can trap it... Could be worth a try (if you're still using SQLite.swift that is...?)