Process triggered by Vapor server returns an empty string

Viewed 41

I have vapor route that accepts a photo and runs an ML model on that photo. The problem I am facing is that the function returns an empty string. This must have something to do with the function returning too quickly and the process taking longer than expected. But I am returning and eventLoopFuture. So I am confused as to why that is happening. Any help would be welcome.

        app.post("upload") { req -> EventLoopFuture<String> in
        struct Input: Content {
            var file: File
            var uid: String
            var width: Int
            var height: Int
        }
        let input = try req.content.decode(Input.self)
                
        let path = app.directory.publicDirectory + input.file.filename
        
        print("file:" + input.file.filename + " uid: " + input.uid)
                
        return req.application.fileio.openFile(path: path,
                                               mode: .write,
                                               flags: .allowFileCreation(posixMode: 0x744),
                                               eventLoop: req.eventLoop)
            .flatMap { handle in
                req.application.fileio.write(fileHandle: handle,
                                             buffer: input.file.data,
                                             eventLoop: req.eventLoop)
                    .flatMapThrowing { _ in
                        try handle.close()
                        
                        let task = Process()
                        task.launchPath = "/usr/local/bin/terminal_machine_learning"
                        task.arguments = [path]

                        let pipe = Pipe()
                        task.standardOutput = pipe
                        try! task.run()
                        let data = pipe.fileHandleForReading.readDataToEndOfFile()

                        let string = String(data: data, encoding: String.Encoding.utf8)!
    
                        task.waitUntilExit()

                        return string
                    }
            };
    }
1 Answers

I think there are several problems here. Firstly, you need to run this off the main thread or you will run into performance issues in production. Also, I remember encountering something similar a few months ago when something changed in vapor. I had to stop using task.launchPath and use task.executableURL instead. I would also replace try! with just try and the task.waitUntilExit() is redundant. So, your code becomes:

return self.application.threadPool.runIfActive(eventLoop: self.eventLoop) { () in
    return req.application.fileio.openFile(path: path,
                                           mode: .write,
                                           flags: .allowFileCreation(posixMode: 0x744),
                                           eventLoop:req.eventLoop)
        .flatMap { handle in
            req.application.fileio.write(fileHandle: handle,
                                         buffer: input.file.data,
                                         eventLoop: req.eventLoop)
            .flatMapThrowing { _ in
                try handle.close()
                        
                let task = Process()
                let launchPath = "/usr/local/bin/terminal_machine_learning"
                task.executableURL = URL(string: "file://\(launchPath)")
                task.arguments = [path]

                let pipe = Pipe()
                task.standardOutput = pipe
                try task.run()
                let data = pipe.fileHandleForReading.readDataToEndOfFile()
                let string = String(data: data, encoding: String.Encoding.utf8)!
                return string
            }
        }
    }
}
Related