PostgresORM.swift 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. /*
  2. * Copyright IBM Corporation 2018
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Foundation
  17. import Dispatch
  18. import LoggerAPI
  19. import SwiftKuery
  20. import SwiftKueryORM
  21. import KueryPostgres
  22. import TechEmpowerCommon
  23. // ORM conformance
  24. extension RandomRow: Model {
  25. public static var tableName: String { return "world" }
  26. }
  27. // ORM conformance
  28. extension Fortune: Model {
  29. public static var tableName: String { return "fortune" }
  30. }
  31. // Configure our ORM Database connection pool as dbConnPool created by KueryPostgres
  32. public func setupORM() {
  33. Database.default = Database(dbConnPool)
  34. }
  35. /// Get a list of Fortunes from the database.
  36. ///
  37. /// - Parameter callback: The callback that will be invoked once the DB query
  38. /// has completed and results are available, passing an
  39. /// optional [Fortune] (on success) or RequestError on
  40. /// failure.
  41. ///
  42. public func getFortunes(callback: @escaping ([Fortune]?, RequestError?) -> Void) -> Void {
  43. Fortune.findAll { (fortunes, err) in
  44. if let err = err {
  45. return callback(nil, err)
  46. } else {
  47. callback(fortunes, nil)
  48. }
  49. }
  50. }
  51. /// Get a random row (range 1 to 10,000) from the database.
  52. ///
  53. /// - Parameter callback: The callback that will be invoked once the DB query
  54. /// has completed and results are available, passing an
  55. /// optional RandomRow (on success) or RequestError on
  56. /// failure.
  57. ///
  58. public func getRandomRow(callback: @escaping (RandomRow?, RequestError?) -> Void) -> Void {
  59. // Select random row from database range
  60. let rnd = RandomRow.randomId
  61. RandomRow.find(id: rnd, callback)
  62. }
  63. /// Updates a row of World to a new value.
  64. ///
  65. /// - Parameter callback: The callback that will be invoked once the DB update
  66. /// has completed, passing an optional RequestError if the
  67. /// update failed.
  68. ///
  69. public func updateRow(id: Int, callback: @escaping (RequestError?) -> Void) -> Void {
  70. // Generate a random number for this row
  71. let row = RandomRow(id: id, randomNumber: RandomRow.randomValue)
  72. row.update(id: id) { (resultRow, err) in
  73. if let err = err {
  74. return callback(err)
  75. } else {
  76. callback(nil)
  77. }
  78. }
  79. }
  80. /// Get `count` random rows from the database, and pass the resulting array
  81. /// to a completion handler (or a RequestError, in the event that a row could
  82. /// not be retrieved).
  83. ///
  84. /// - Parameter count: The number of rows to retrieve
  85. /// - Parameter result: The intermediate result array being built
  86. /// - Parameter completion: The closure to invoke with the result array, or error
  87. ///
  88. public func getRandomRows(count: Int, result: [RandomRow] = [], completion: @escaping ([RandomRow]?, RequestError?) -> Void) {
  89. if count > 0 {
  90. // Select random row from database range
  91. RandomRow.find(id: RandomRow.randomId) { (resultRow, err) in
  92. if let resultRow = resultRow {
  93. var result = result
  94. result.append(resultRow)
  95. getRandomRows(count: count-1, result: result, completion: completion)
  96. } else {
  97. if let err = err {
  98. completion(nil, err)
  99. } else {
  100. fatalError("Unexpected: result and error both nil")
  101. }
  102. }
  103. }
  104. } else {
  105. completion(result, nil)
  106. }
  107. }
  108. /// A parallel version of `getRandomRows` that invokes each get in parallel, builds an
  109. /// array of results and waits for each get to complete before returning.
  110. ///
  111. /// - Parameter count: The number of rows to retrieve
  112. /// - Parameter completion: The closure to invoke with the result array, or error
  113. ///
  114. public func getRandomRowsParallel(count: Int, completion: @escaping ([RandomRow]?, RequestError?) -> Void) {
  115. var results: [RandomRow] = []
  116. guard count > 0 else {
  117. return completion(results, nil)
  118. }
  119. // Used to protect result array from concurrent modification
  120. let updateLock = DispatchSemaphore(value: 1)
  121. // Execute each query. Each callback will append its result to `results`
  122. for _ in 1...count {
  123. RandomRow.find(id: RandomRow.randomId) { (resultRow, err) in
  124. guard let resultRow = resultRow else {
  125. Log.error("\(err ?? .internalServerError)")
  126. completion(nil, err ?? .internalServerError)
  127. return
  128. }
  129. updateLock.wait()
  130. results.append(resultRow)
  131. if results.count == count {
  132. completion(results, nil)
  133. }
  134. updateLock.signal()
  135. }
  136. }
  137. }
  138. /// Update and retrieve `count` random rows from the database, and pass the
  139. /// resulting array to a completion handler (or a RequestError, in the event
  140. /// that a row could not be retrieved or updated).
  141. ///
  142. /// - Parameter count: The number of rows to retrieve
  143. /// - Parameter result: The intermediate result array being built
  144. /// - Parameter completion: The closure to invoke with the result array, or error
  145. ///
  146. public func updateRandomRows(count: Int, result: [RandomRow] = [], completion: @escaping ([RandomRow]?, RequestError?) -> Void) {
  147. if count > 0 {
  148. // Select random row from database range
  149. RandomRow.find(id: RandomRow.randomId) { (resultRow, err) in
  150. if let resultRow = resultRow {
  151. var result = result
  152. let row = RandomRow(id: resultRow.id, randomNumber: RandomRow.randomValue)
  153. row.update(id: row.id) { (resultRow, err) in
  154. if let resultRow = resultRow {
  155. result.append(resultRow)
  156. updateRandomRows(count: count-1, result: result, completion: completion)
  157. } else {
  158. completion(nil, err)
  159. }
  160. }
  161. } else {
  162. if let err = err {
  163. completion(nil, err)
  164. } else {
  165. fatalError("Unexpected: result and error both nil")
  166. }
  167. }
  168. }
  169. } else {
  170. completion(result, nil)
  171. }
  172. }
  173. /// A parallel version of `updateRandomRows` that invokes each get/update operation
  174. /// in parallel, builds an array of results and waits for each get to complete before
  175. /// returning.
  176. ///
  177. /// - Parameter count: The number of rows to retrieve
  178. /// - Parameter completion: The closure to invoke with the result array, or error
  179. ///
  180. public func updateRandomRowsParallel(count: Int, completion: @escaping ([RandomRow]?, RequestError?) -> Void) {
  181. var results: [RandomRow] = []
  182. guard count > 0 else {
  183. return completion(results, nil)
  184. }
  185. // Used to protect result array from concurrent modification
  186. let updateLock = DispatchSemaphore(value: 1)
  187. // Execute each query. Each callback will append its result to `results`
  188. for _ in 1...count {
  189. RandomRow.find(id: RandomRow.randomId) { (resultRow, err) in
  190. guard let resultRow = resultRow else {
  191. Log.error("\(err ?? .internalServerError)")
  192. completion(nil, err ?? .internalServerError)
  193. return
  194. }
  195. let row = RandomRow(id: resultRow.id, randomNumber: RandomRow.randomValue)
  196. row.update(id: row.id) { (resultRow, err) in
  197. if let resultRow = resultRow {
  198. updateLock.wait()
  199. results.append(resultRow)
  200. if results.count == count {
  201. completion(results, nil)
  202. }
  203. updateLock.signal()
  204. } else {
  205. Log.error("\(err ?? .internalServerError)")
  206. completion(nil, err ?? .internalServerError)
  207. return
  208. }
  209. }
  210. }
  211. }
  212. }