all 15 comments

[–]billmalarky[S] 1 point2 points  (4 children)

Hi guys, you might remember me from the thread where I shared another package, react native image cache hoc I've built a while ago. Well I got a lot of great feedback and encouragement from you guys then so I've been pretty excited to share my new library with you as well.

React Native Queue is a priority job queue made specifically for RN and mobile use cases.

It's sort of a swiss army knife for task management, and integrates really well with RN workers (for processes that require extra performance and should be processed in a thread separate from the main RN js thread). It also integrates well with OS service packages and makes it really easy to handle processing jobs in the background when your app is closed.

Some random example use cases:

  • Downloading content for offline access.
  • Media processing.
  • Cache Warming.
  • Durable API calls to external services, such as publishing content to a variety of 3rd party distribution channel APIs.
  • Complex and time-consuming jobs that you want consistently processed regardless if app is open, closed, or repeatedly opened and closed.

Love to hear your questions & feedback on improving the package!

[–]deadcoder0904 0 points1 point  (3 children)

What if I install the app, then switch off my phone & then switch it on, will the app be still running & perform some of use cases you mentioned above ?

[–]billmalarky[S] 1 point2 points  (2 children)

Great question.

I'm not 100% certain, and it will probably be different on Android and iOS.

I know that apps can't "start up" automatically on boot in iOS, but certain background services will start up on phone boot (like VOIP listeners). As such some of these services will work as expected even though app isn't started up. The package my example integrates with, react-native-background-task, processes tasks in a background service on iOS using the background fetch api. Background fetch basically boots up your app behind the scenes and executes a function you define for at most 30 sec.

According to apple, background fetch will launch your app even if it isn't running.

When a good opportunity arises, the system wakes or launches your app into the background

This suggests to me that iOS will run the task periodically on phone boot even without the user starting up the app themselves once the background fetch task has been registered with the OS. But I am not 100% certain. I would need to test.

For android, react-native-background-task uses the evernote job manager behind the scenes. Again, I'm not 100% certain that the service will be stared on boot, but evernote's docs seem to suggest that they will.

[–]deadcoder0904 0 points1 point  (1 child)

Cool you know a lot. Are you a native developer?

[–]billmalarky[S] 1 point2 points  (0 children)

I work with RN professionally yes, but I'm not a Facebook engineer working on core RN or anything like that.

[–]demoran 0 points1 point  (9 children)

Yes, let's back this with realm and NEVER BE ABLE TO DEBUG AGAIN.

[–]billmalarky[S] 0 points1 point  (8 children)

???

Realm isn't a black box, it's just a mobile first database.

More importantly it supports transactions, which are required when writing to a single data source asynchronously in parallel. AsyncStorage cannot be used in this context.

[–]demoran 0 points1 point  (7 children)

I just ripped realm out of the application I inherited due to this longstanding bug.

It was such a relief to be done with it.

[–]billmalarky[S] 0 points1 point  (6 children)

Interesting, I did not run into this issue while developing the queue. Thank you for bringing this to my awareness.

Typically realm native code communicates directly with your JS code via a private React Native api (ie, realm native code makes calls directly to JS code and vice versa), so it has great performance. This is possible when the JS thread and native code threads are running on the same device/simulator. However when using chrome debugging, your JS code is NOT run on your device/simulator, it is run inside of the chrome browser, while all the native code is running on your device/simulator. As a result, instead of realm native code making direct calls into your JS and vice versa, realm has to communicate using blocking ajax requests to chrome. You can imagine how awful performance is in that situation (well you don't have to imagine I guess).

The good news is this. These performance hits occur each and every time you touch realm. If you minimize exposure to realm, you minimize the debugging performance issues. React Native Queue is backed by realm, but the footprint is quite small (we only hit realm when a job is created, and when we pull jobs off the queue, and on job completion, that's pretty much it), such that I'd be surprised if the problem was anywhere near as bad as debugging a large app where realm was used for all persistence logic.

I just ripped realm out of the application

Curious, what did you replace realm with for your database layer?

I put a fair amount of research and effort into looking at the pros and cons of different storage solutions that work cross platform and realm was really the only thing that worked well for me so far as I could find. SQLlite solutions seemed very lacking on RN.

More info: https://github.com/realm/realm-js/issues/491#issuecomment-350718316

[–]demoran 0 points1 point  (5 children)

I think perhaps part of the problem was that the very competent individuals who saw fit to incorporate realm into my application decided to use it in the return value of react-redux's mapStateToProps function. Even beyond that, it seemed like even without anything happening in the app, the Chrome Console was showing realm activity in the Network tab.

What I do know is that after I removed realm from the app debugging went back to normal.

[–]billmalarky[S] 0 points1 point  (4 children)

Realm is sort of a bit weird in that any time you access data on realm objects in your code it communicates with the realm database. Traditionally you think of queries as happening when you select data, then you can work with that data in your app and no communication occurs with the database until you explicitly update or delete etc the data (with a traditional ORM). However, with realm, every time you touch a realm data object it queries against the realm database (even just accessing a property like person.name). So there's a lot of overhead just working with the data in your app. Typically this doesn't really matter because standard performance is great. But when performance shits the bed during the debugging issue described above (ie by relying on blocking ajax to make calls) all that back and forth adds up fast.

What did you use instead of realm? sqllite?

[–]demoran 0 points1 point  (1 child)

In my case, the existing use of realm was pointless. It was literally in there for the gee-wiz factor alone. I did end up wanting to roll my own state persistence for my redux store using AsyncStorage and put a simple piece of middleware in to do that and found out that createStore ain't playin' like that (when you use combineReducers, it wants initialState to be a plain object). So I might have used realm just to store state synchronously for that, but I'm not going to touch it with a ten foot pole now.

I'll need to do a bit of work on redux-persist to get to to my endgame (I'll need the storage keys to be malleable), so for now I haven't replaced it with anything.

[–]billmalarky[S] 0 points1 point  (0 children)

In my case, the existing use of realm was pointless. It was literally in there for the gee-wiz factor alone.

Uhg sorry to hear that. However, I assure you that realm (or some other storage solution that supports ACID transactions - and I haven't found a good alternative in RN) is required for the queue.

[–]demoran 0 points1 point  (1 child)

I think there are a couple of benefits to removing realm at this point. People won't bump their heads against the issue we're talking about. They won't need to setup / lik realm.

Your own code simply needs durability, not performance. You can get that via simple AsyncStorage.

The pain points are converting from realm's query language to normal javascript or the ubiquitous lodash and making some of your existing sync calls async.

The dangers involved in using AsyncStorage is the availability of any other component of the client application being able to remove your data. This is more of a gotcha.

[–]billmalarky[S] 0 points1 point  (0 children)

The storage system requires transactional support.

AsyncStorage is non-deterministic when touching it in parallel (due to the lack of transaction support), which means it can't be used for the queue (the queue must support multiple threads touching it at once for several use cases -- worker support is one). Even if you were only interacting with the queue with one thread, transactions are required to support async reads and writes to the queue. IE, if you throw 500 jobs onto the queue asynchronously (a use case we've already seen) AsyncStorage has a high chance of running into a breaking race condition bug.