Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some questions on GRDB.swift's concurrency - Is GRDB code runs in caller thread (Can be main thread or user thread)? #588

Closed
yccheok opened this issue Aug 6, 2019 · 1 comment

Comments

@yccheok
Copy link

yccheok commented Aug 6, 2019

Thank you for creating this wonderful library.

What did you do?

Understand the concurrency mechanism used by GRDB.swift

What did you expect to happen?

By reading https://github.com/groue/GRDB.swift/blob/master/README.md#concurrency

Guarantee 1: writes are always serialized. At every moment, there is no more than a single thread that is writing into the database.

Based on the above reading, I'm assuming GRDB.swift library itself is including a single thread executor. Whenever there is a write operation, my imaginary of GRDB.swift code is as follow (Sorry. I'm from Java background)

// global_executor is a Executors.newSingleThreadExecutor();
global_executor.submit(writeOperationRunnable);

What happened instead?

If I run the following Demo https://github.com/groue/GRDB.swift/blob/master/DemoApps/GRDBDemoiOS/GRDBDemoiOS/AppDatabase.swift with the following minor modification (To understand whether the thread is UI thread or user thread)

        migrator.registerMigration("createPlayer") { db in
            print("1. Is main thread \(Thread.isMainThread)")
            
            // Create a table
            // See https://github.com/groue/GRDB.swift#create-tables
            try db.create(table: "player") { t in
                
                print("2. Is main thread \(Thread.isMainThread)")
                
                t.autoIncrementedPrimaryKey("id")
                
                // Sort player names in a localized case insensitive fashion by default
                // See https://github.com/groue/GRDB.swift/blob/master/README.md#unicode
                t.column("name", .text).notNull().collate(.localizedCaseInsensitiveCompare)
                
                t.column("score", .integer).notNull()
            }
        }

I get the following output

  1. Is main thread true
  2. Is main thread true
  1. Seems like my early assumption is wrong. That's mean, GRDB is performing operation, based on the caller's thread? (Caller thread can be either main thread or user thread)
  2. If so, how does GRDB.swift achieves "single thread that is writing into the database."? Does GRDB internally use a reader/writer lock?

I always have a strong concern, that I want to avoid performing I/O bounded operations (Be it networking, database accessing or file read/write) in Main thread.

In my Android Java code, I write them the following pattern.

    public void delete(Backup backup) {
        // Android Room
        LocalBackupRoomDatabase.instance().runInTransaction(() -> {
            BackupDao backupDao = LocalBackupRoomDatabase.instance().backupDao();
            backupDao.delete(backup);
        });
    }
   
    public void deleteAsync(Backup backup) {
        getExecutorForRepository().execute(() -> {
            // Performing I/O bounded operation in user thread
            delete(backup);
        });
    }

    private static final ExecutorService executorForRepository = Executors.newSingleThreadExecutor();

    public static Executor getExecutorForRepository() {
        return executorForRepository;
    }

So, if I were using GRDB code, I believe I can refactor the code to equivalent swift code ?

    public void delete(Backup backup) {
        // GRDB code......
    }
   
    public void deleteAsync(Backup backup) {
        getExecutorForRepository().execute(() -> {
            // Performing I/O bounded operation in user thread
            delete(backup);
        });
    }

    private static final ExecutorService executorForRepository = Executors.newSingleThreadExecutor();

    public static Executor getExecutorForRepository() {
        return executorForRepository;
    }

Environment

GRDB flavor(s): GRDB
GRDB version: 4.1.0
Installation method: Manual
Xcode version: 11.0 beta
Swift version: 5.1
Platform(s) running GRDB: (iOS, macOS, watchOS?)
macOS version running Xcode:

@groue
Copy link
Owner

groue commented Aug 6, 2019

GRDB uses Dispatch Queues as support for its concurrency guarantees. Dispatch Queues guarantee strict serialization, without being bound to any particular thread. This is quite unlike what you may know from your Java background: click the link above and get familiar with this fundamental Apple technology. You will meet it again if you start developing on Apple platforms.

I advise you put aside your Java background, and look at those technology with a fresh eye, without assuming anything. This will spare you hours of misinterpretation.

I always have a strong concern, that I want to avoid performing I/O bounded operations (Be it networking, database accessing or file read/write) in Main thread.

Learning about Dispatch Queues will generally teach you how to escape the main thread. Also, have a look at GRDB asynchronous APIs. And read again the GRDBCombine documentation, since this is where you come from: it tells how to avoid the main thread when you want it.

Now @yccheok, please listen very carefully: this issue is one of many you have recently asked about GRDB and GRDBCombine, first asking how it works, and later questioning its very fundamentals. Answering your questions takes time, for the benefit of very few.

I suggest you try another strategy: read, look for open and closed issues, and pull requests. This willl show you the kind of problem people have with GRDB. These are not the problems you imagine.

@groue groue closed this as completed Aug 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants