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

[Question] How to know when batch syncing is busy. #2165

Open
louwjlabuschagne opened this issue Oct 9, 2024 · 7 comments
Open

[Question] How to know when batch syncing is busy. #2165

louwjlabuschagne opened this issue Oct 9, 2024 · 7 comments

Comments

@louwjlabuschagne
Copy link

Summary

We are encountering a HTTPService is busy error when attempting to call our custom endpoint that utilizes axios. This error occurs because background syncing (handled by the background-geolocation library) is still processing while we attempt to make the call. We are looking for a way to detect when the batch syncing process is in progress—ideally by subscribing to an isLoading state or some other observable mechanism. This would allow us to handle the API call more effectively and avoid conflicts with ongoing sync operations.

Your Environment

  • Plugin version: 4.17.1
  • Platform: iOS and Android
  • OS version: All supported versions
  • Device manufacturer/model: All devices
  • React Native version: 0.73.6

Plugin Configuration

Here’s our current configuration setup for the background geolocation service:

await BackgroundGeolocation.ready({
  httpRootProperty: ".",
  locationTemplate:
    '{"age":"<%= age %>","accuracy":"<%= accuracy %>","longitude":"<%= longitude %>","latitude":"<%= latitude %>","timestamp":"<%= timestamp %>","batteryLevel":"<%= battery.level %>","odometer":"<%= odometer %>"}',
  reset: true,
  debug: false,
  logLevel: BackgroundGeolocation.LOG_LEVEL_OFF,
  desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_NAVIGATION,
  distanceFilter: Platform.OS === "android" ? 0 : 5,
  locationUpdateInterval: 1000,
  fastestLocationUpdateInterval: 10000,
  autoSync: true,
  autoSyncThreshold: 50,
  batchSync: true,
  maxDaysToPersist: 14,
  maxBatchSize: 500,
  enableHeadless: true,
  disableLocationAuthorizationAlert: true,
  disableElasticity: true,
  pausesLocationUpdatesAutomatically: false,
  disableMotionActivityUpdates: true,
  heartbeatInterval: 60,
  stopOnTerminate: Platform.select({ default: true, android: false }),
  showsBackgroundLocationIndicator: true,
  stopOnStationary: false,
  backgroundPermissionRationale: {
    title: "Permission required",
    message: "This app needs background location access to track your activity while the app is not open.",
  },
});

// Additional configuration when recording starts:
await BackgroundGeolocation.setConfig({
  url: `${beConfig.config?.tracking?.serverUrl}/batch`,
  authorization: {
    strategy: 'JWT',
    accessToken: idToken!,
  },
  extras: {
    roundId: roundQuery.data?.id!,
    versionNumber: `${VersionNumber.appVersion}(${VersionNumber.buildVersion})`,
  },
});

Expected Behavior

We expect the batch syncing process not to interfere with our API calls. Specifically, we would like to know when the background sync is in progress (via a state like isLoading or a similar mechanism) so we can avoid triggering the HTTPService is busy error. While the onHttp method allows us to receive responses after sync requests, we are looking for a way to be informed when requests are being sent in the first place.

Actual Behavior

During the call to our API, we intermittently receive the HTTPService is busy error. This is likely due to the background syncing process initiated by the background-geolocation library, which may be blocking the axios thread.

Here’s the code that triggers the error:

const onRoundCompletedAsync = async () => {
  try {
    await BackgroundGeolocation.sync();
    await endRoundQuery.mutateAsync();

    await BackgroundGeolocation.setConfig({
      isMoving: false,
    });
    await BackgroundGeolocation.stop();

    queryClient.invalidateQueries(['user_profile']);
    queryClient.invalidateQueries(['user_balance']);
  } catch (error) {
    // This is where we encounter the `HTTPService is busy` error
    console.error('Error during round completion:', error);
  }
}

Steps to Reproduce

1.	Configure BackgroundGeolocation with the settings above, ensuring batchSync: true.
2.	Accumulate sufficient GPS points (over 1500) so that batch sync is triggered during API calls.
3.	Call await BackgroundGeolocation.sync(); and observe that the error HTTPService is busy occurs.

Context

We are trying to determine when the background geolocation service is actively syncing data, so that we can manage API calls more intelligently and avoid conflicts. The ideal solution would involve subscribing to a sync state or knowing precisely when the batch sync process is sending requests.

Debug logs

Here’s an excerpt of the logs where the issue arises:

Logs Error: HTTPService is busy
@christocracy
Copy link
Member

we are looking for a way to be informed when requests are being sent in the first place.

Why? What would you do differently? If the http service is busy, that means it’s currently uploading records, which is what you set out to do by calling .sync().

“HTTP Service is busy” is not an error. It’s more like “I’m already on it”. It’s completely harmless.

@louwjlabuschagne
Copy link
Author

Just want to confirm.

If we call .sync and get “HTTP Service is busy”, we can ignore it and we continue with our logic, which involves calling await BackgroundGeolocation.stop(); as shown above - the batch syncing won't stop or have a problem?

So we can continue our business logic and the package will ensure all datapoints are sync even if .stop was called?

@christocracy
Copy link
Member

  • the batch syncing won't stop or have a problem?

No. The HTTP service is independent. Once started, it won’t stop until all records in the database are emptied. There is only ever one instance of the http service operating at any given time, posting records synchronously.

  • select record
  • Post
  • Response
  • Repeat

So we can continue our business logic

Yes.

@louwjlabuschagne
Copy link
Author

Follow-up Question: Is .sync Necessary?

I wanted to clarify whether we actually need to manually call .sync. My understanding is that the batch syncing mechanism will automatically sync data as needed, so there shouldn’t be a need to force a sync.

Here’s the setup when we start a “round”:

await BackgroundGeolocation.setConfig({
  url: `${beConfig.config?.tracking?.serverUrl}/batch`,
  authorization: {
    strategy: 'JWT',
    accessToken: idToken!,
  },
  extras: {
    roundId: roundQuery.data?.id!,
    versionNumber: `${VersionNumber.appVersion}(${VersionNumber.buildVersion})`,
  },
});

At the end of the round (typically after 4 hours), we stop recording GPS data.

What I’d like to confirm is whether we need to worry about extras.roundId lingering in the database. Specifically, will the records from the previous round persist with the old roundId? When a new round starts, will the new extras.roundId be applied to new records, without requiring a .sync to ensure the previous round’s records are fully synced? Can we rely on the batch sync mechanism to handle everything as we continue recording new data?

@Chugunenok
Copy link

Once started, it won’t stop until all records in the database are emptied.

This causes bad behavior: if http request takes a long time, then BackgroundGeolocation.sync() will not stop until you do BackgroundGeolocation.stop(). While http client syncs a single point to server (http request can take more than 1 second), we get new point (iOS sends actual point every ~1 second). It will never stop sync if http request is slower than the period of getting updated location.
When I call BackgroundGeolocation.sync(), I expect it to sync only points collected until current time.
If I want to sync everything - I will do while(pointsExists) {await BackgroundGeolocation.sync(); pointsExists = ...} loop manually.
We have a custom GPS data processing on server and we want to collect ALL points. And we want to sync points at least every 10 minutes. Now the only way to do this is to manually read points -> send to server -> delete points.

@christocracy
Copy link
Member

Perhaps you're interested in Config.batchSync, Config.batchSyncThreshold and Config.maxBatchSize. Also see the API docs HTTP Guide

@Chugunenok
Copy link

Config.batchSyncThreshold

Do you mean autoSyncThreshold? I cannot use autoSyncThreshold because I need to send data every 10 minutes regardless of collected points amount.
I am using batchSync=true and maxBatchSize=1000.
Every 10 minutes I run BackgroundGeolocation.sync(), it sends several 1000 points batches and then starts sending 1-2 points batches continuously, it stops only when iOS is providing new points slower than library syncs it to server.

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

3 participants