I'm struggling with understanding how to perform a batch save of fetched objects and store them to the database. After I have stored the objects to the database I want to return the result of a query.
I can't understand how to do this with EventLoopFuture since when I call .wait()
I get the error message:
Precondition failed: BUG DETECTED: wait() must not be called when on an EventLoop.
As an example of my problem:
This is what I got so far, but which gives me the bug:
func flights(on conn: DatabaseConnectable, customerName: String, flightType: FlightType) throws -> Future<[Flight]> {
return Airport.query(on: conn).filter(\.customerName == customerName).first().flatMap(to: [Flight].self) { airport in
guard let airport = airport else {
throw Abort(.notFound)
}
guard let airportId = airport.id else {
throw Abort(.internalServerError)
}
// Update items for customer
let fetcher: AirportManaging?
switch customerName.lowercased() {
case "coolCustomer":
fetcher = StoreOneFetcher()
default:
fetcher = nil
debugPrint("Unhandled customer to fetch from!")
// Do nothing
}
let completion = Flight.query(on: conn).filter(\.airportId == airportId).filter(\.flightType == flightType).all
guard let flightFetcher = fetcher else { // No customer fetcher to get from, but still return whats in the DB
return completion()
}
return try flightFetcher.fetchDataForAirport(customerName, on: conn).then({ (flights) -> EventLoopFuture<[Flight]> in
flights.forEach { flight in
_ = try? self.storeOrUpdateFlightRecord(flight, airport: airport, on: conn).wait()
}
return completion()
})
}
}
func storeOrUpdateFlightRecord(_ flight: FetcherFlight, airport: Airport, on conn: DatabaseConnectable) throws -> EventLoopFuture<Flight> {
guard let airportId = airport.id else {
throw Abort(.internalServerError)
}
return Flight.query(on: conn).filter(\.itemName == flight.itemName).filter(\.airportId == airportId).filter(\.flightType == flight.type).all().flatMap(to: Flight.self) { flights in
if let firstFlight = flights.first {
debugPrint("Found flight in database, updating...")
return flight.toFlight(forAirport: airport).save(on: conn)
}
debugPrint("Did not find flight, saving new...")
return flight.toFlight(forAirport: airport).save(on: conn)
}
}
So the problem in on line _ = try? self.storeOrUpdateFlightRecord(flight, airport: airport, on: conn).wait()
. I can't call wait()
since it'll block the eventLoop but if I call map
or flatMap
I need in turn return an EventLoopFuture<U>
(U
being Flight
), which I am totally uninterested in.
I want self.storeOrUpdateFlightRecord
to be called and the result ignored. How can I do that?
Yeah, you can't use .wait()
on eventLoop
.
In your case you could use flatten
for batch operations
/// Flatten works on array of Future<Void>
return flights.map {
try self.storeOrUpdateFlightRecord($0, airport: airport, on: conn)
/// so transform a result of a future to Void
.transform(to: ())
}
/// then run flatten, it will return Future<Void> as well
.flatten(on: conn).flatMap {
/// then do what you want :)
return completion()
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With