Quaestiones batch interrogationis processus eorumque solutiones (pars 1)

Quaestiones batch interrogationis processus eorumque solutiones (pars 1)Fere omnia moderna producta programmata ex pluribus officiis constant. Saepe, longa responsionis tempora canalium interstitiorum fons effectionis problematum fiunt. Regula huius quaestionis solutio est multiplices petitiones interservire in unam sarcinam, quae batching appellatur.

Si batch processus uteris, felix esse non potes cum effectibus faciendis vel codicis claritate. Haec ratio non tam facilis est in RECENS ut putes. Pro diversis propositis ac diversis adiunctis, solutiones valde variari possunt. Speciis exemplis utens, plurium aditus pros et cons ostendam.

Demonstratio project

Ad evidentiam, inspiciamus exemplum cuiusdam muneris in applicatione quod nunc sum operantes in.

Explicatio platform lectio ad exemplaProblema de observantia pauperum est omnino generalis et nullis linguis specificis vel suggestis non afficit. Hic articulus veris + Kotlin exemplis utetur ad quaestiones et solutiones demonstrandas. Kotlin aeque comprehensibile (vel incomprehensibile) ad tincidunt Java et C#, praeterea codicem magis compactum et intellegibile est quam in Java. Ut facilius intelligas pro tincidunt Java puro, magicam nigram Kotlin vitabo et tantum magia alba alba (in spiritu Lombok). Pauci modi extensionis erunt, sed actu familiares omnibus programmatibus Javae ut statice rationibus, hoc erit saccharum parvum, quod gustum catini non corrumpet.
Documentum est officium approbationis. Aliquis documentum gignit et illud ad disceptandum submittit, in quo factae sunt emendationes, ac demum documentum convenit. Approbatio ipsius officii nihil de documentis scit: est sermo probatorum cum parvis muneribus additis quas hic non consideramus.

Itaque camerae sunt chat (congruis documentis) cum praefinito ordine participantium in unaquaque earum. Sicut in colloquiis regularibus, nuntii textum et limam continent ac responsa vel in priorem possunt esse:

data class ChatMessage(
  // nullable так как появляется только после persist
  val id: Long? = null,
  /** Ссылка на автора */
  val author: UserReference,
  /** Сообщение */
  val message: String,
  /** Ссылки на аттачи */
  // из-за особенностей связки JPA+СУБД проще поддерживать и null, и пустые списки
  val files: List<FileReference>? = null,
  /** Если является ответом, то здесь будет оригинал */
  val replyTo: ChatMessage? = null,
  /** Если является пересылкой, то здесь будет оригинал */
  val forwardFrom: ChatMessage? = null
)

Fasciculi et usoris nexus sunt nexus aliis ditionibus. Hic vivimus sic:

typealias FileReference Long
typealias UserReference Long

Usoris notitia in Keycloak reposita est et per quietem recepta. Idem valet de fasciculis: fasciculis et metainformationum de eis in separato instrumento tabellariorum vivunt.

Omnes vocat ad haec officia sunt gravibus petitiones. Hoc significat caput transportandi petitiones multo maior quam tempus est ut processus per ministerium partium tertiarum. In scamnis nostris examinis, vocatio typica tempus talium officiorum 100 ms est, ut his numeris in futurum utemur.

Nos postulo ut moderatorem QUIETIS simplicis ad ultimas N epistulas recipiendas cum omnibus rebus necessariis notitiis ad nos perducamur. Hoc est, credimus nuntium exemplar in fronte idem fere esse ac notitias omnes mittendas esse. Differentia inter exemplar ante-finis est quod tabella et usor necesse est exhiberi in forma aliquantulum decrypta ut nexus eos efficiat:

/** В таком виде отдаются ссылки на сущности для фронта */
data class ReferenceUI(
  /** Идентификатор для url */
  val ref: String,
  /** Видимое пользователю название ссылки */
  val name: String
)
data class ChatMessageUI(
  val id: Long,
  /** Ссылка на автора */
  val author: ReferenceUI,
  /** Сообщение */
  val message: String,
  /** Ссылки на аттачи */
  val files: List<ReferenceUI>,
  /** Если являтся ответом, то здесь будет оригинал */
  val replyTo: ChatMessageUI? = null,
  /** Если являтся пересылкой, то здесь будет оригинал */
  val forwardFrom: ChatMessageUI? = null
)

Non opus est ut sequentia efficiant:

interface ChatRestApi {
  fun getLast(nInt): List<ChatMessageUI>
}

UI postfixam significat DTO exempla in frontend, id est, quod per CAETERA servire debeamus.

Quid mirum hic est quod nos nullum chat ID praetereuntes nec exemplar ChatMessageUI ChatMessageUI unum non habemus. Hoc studiose feci ut codicem exemplorum non clunes (sermones remotos, ut unum tantum habere possimus).

Digressio philosophicaTum Classis ChatMessageUI et ChatRestApi.getLast methodus utendi indice notitiarum genere cum re vera ordinatum est Set. Hoc in JDK malum est, ita ordinem elementorum in gradu interfaciei declarans (ordinem addito et removendo servando) non laborabit. Ita consuetudo communis facta est ut Indicem in casibus ubi ordinatum Set opus est (est etiam LinkedHashSet, sed hoc interfacies non est).
Limitatio momenti: Ponemus nullas longas responsionum vincula aut translationes. Hoc est, sunt, sed longitudo earum tres nuntios non excedit. Tota catena nuntiorum in frontend tradenda est.

Ad notitias ab externis officiis recipiendas APIs sequentia sunt:

interface ChatMessageRepository {
  fun findLast(nInt): List<ChatMessage>
}
data class FileHeadRemote(
  val id: FileReference,
  val name: String
)
interface FileRemoteApi {
  fun getHeadById(idFileReference): FileHeadRemote
  fun getHeadsByIds(idSet<FileReference>): Set<FileHeadRemote>
  fun getHeadsByIds(idList<FileReference>): List<FileHeadRemote>
  fun getHeadsByChat(): List<FileHeadRemote>
}
data class UserRemote(
  val id: UserReference,
  val name: String
)
interface UserRemoteApi {
  fun getUserById(idUserReference): UserRemote
  fun getUsersByIds(idSet<UserReference>): Set<UserRemote>
  fun getUsersByIds(idList<UserReference>): List<UserRemote>
}

Videri potest exteriora officia initio processus batch providere, et in utraque versione: per Set (sine conservato elementorum ordine, cum unicis clavibus) et per Indicem (fortasse duplicata — ordo servatur).

Simple implementations

Simplex exsecutionem

Prima rudis exsecutio nostrae QUIETIS moderatoris in pluribus aliquid simile spectabit;

class ChatRestController(
  private val messageRepository: ChatMessageRepository,
  private val userRepository: UserRemoteApi,
  private val fileRepository: FileRemoteApi
) : ChatRestApi {
  override fun getLast(nInt) =
    messageRepository.findLast(n)
      .map it.toFrontModel() }
  
  private fun ChatMessage.toFrontModel(): ChatMessageUI =
    ChatMessageUI(
      id = id ?: throw IllegalStateException("$this must be persisted"),
      author = userRepository.getUserById(author).toFrontReference(),
      message = message,
      files = files?.let files ->
        fileRepository.getHeadsByIds(files)
          .map it.toFrontReference() }
      } ?: listOf(),
      forwardFrom = forwardFrom?.toFrontModel(),
      replyTo = replyTo?.toFrontModel()
    )
}

Omnia clarissima sunt, et hoc plus magnum est.

Batch processus utimur et notitias accipimus ab externo servitio in batches. Sed quid fructibus nostris accidit?

Pro unoquoque nuntio, invitatio ad UserRemoteApi fiet ut notitias in campum auctoris recipias et una vocatio ad FileRemoteApi omnia documenta affixa accipias. Quod videtur. Dicamus deinceps inde et respondere ad agros pro ChatMessage ita obtineri ut hoc vocationibus supervacaneis non indigeat. Sed eas in ChatMessageUI convertens ad recursionem ducet, id est, calculis vocationem signanter augere potest. Ut antea notavimus, sumamus nos multum nidificandi et catenam tribus nuntiis limitatam non habere.

Quam ob rem a duobus ad sex vocat ad officia externa per nuntium dabimus et unum JPA pro tota sarcina epistularum petemus. Totalis numerus vocatorum variabit ab 2*N+1 ad 6*N+1. Quantum est hoc in unitates reales? Dicamus 20 nuntia accipere paginam reddere. Ad recipiendos eos capiet de 4 sol- tis ad 10 sol. Terribilis! D intus retinere vellem ma. Et cum somniaverunt inconsutilem scrolling in frontindo facere, duplicari potest ad perficiendum requisita huius rei.

pros,

  1. Codex brevis et auto-documentans (somnium quadrigis sustentans).
  2. Codex simplex est, ut nullae fere occasiones te in pede iaciant.
  3. Batch processus non tamquam res aliena et organice integratur in logicam.
  4. Mutationes logicae facile fient et locales erunt.

minus;

Horribilis effectus ob minimas facis.

Hic aditus satis saepe videri potest in officiis simplicibus vel in prototypis. Si celeritas mutationum mutandarum magni momenti est, vix operae pretium est systema complicare. Eodem tempore, pro nostra simplicissima opera faciendi terribilis est, sic applicabilis ambitus accessus valde angustus est.

Simplex processus parallel

Incipere potes omnia nuntia in parallela dispensando - hoc permittet te augeri lineari tempore secundum numerum epistularum removere. Hoc non est peculiaris via bona, quia in magno culmine oneris exterioris servitii proveniet.

Processus parallelus exsequens valde simplex est:

override fun getLast(nInt) =
  messageRepository.findLast(n).parallelStream()
    .map it.toFrontModel() }
    .collect(toList())

Parallela epistula utendo processui, 300-700 ms specimen praebemus, quod multo melius est quam cum simplici exsecutione, sed tamen non satis celeriter.

Cum hoc aditu, petitiones repositorium et repositorium repositorium synchrone exsecutum erit, quod non est valde efficax. Hoc ut figas, multum debebis mutare logicam. Exempli gratia, via CompletionStage (aka CompletableFuture);

private fun ChatMessage.toFrontModel(): ChatMessageUI =
  CompletableFuture.supplyAsync {
    userRepository.getUserById(author).toFrontReference()
  }.thenCombine(
    files?.let {
      CompletableFuture.supplyAsync {
        fileRepository.getHeadsByIds(files).map it.toFrontReference() }
      }
    } ?: CompletableFuture.completedFuture(listOf())
  ) authorfiles ->
    ChatMessageUI(
      id = id ?: throw IllegalStateException("$this must be persisted"),
      author = author,
      message = message,
      files = files,
      forwardFrom = forwardFrom?.toFrontModel(),
      replyTo = replyTo?.toFrontModel()
    )
  }.get()!!

Videri potest initio simplicis tabulae codicem minus comprehensum factam esse. Et hoc est, quia separare debemus vocat ad officia exteriora unde eventus adhibentur. Hoc per se non est malum. Sed coniunctio vocat non valde elegans et similis est reactivum "noodle typicum".

Si coronis uteris, omnia honestiora spectabunt;

private fun ChatMessage.toFrontModel(): ChatMessageUI =
  join(
    userRepository.getUserById(author).toFrontReference() },
    files?.let fileRepository.getHeadsByIds(files)
      .map it.toFrontReference() } } ?: listOf() }
  ).let (author, files) ->
    ChatMessageUI(
      id = id ?: throw IllegalStateException("$this must be persisted"),
      author = author,
      message = message,
      files = files,
      forwardFrom = forwardFrom?.toFrontModel(),
      replyTo = replyTo?.toFrontModel()
    )
  }

ubi:

fun <ABjoin(a: () -> Ab: () -> B) =
  runBlocking(IO{
    awaitAll(async a() }async b() })
  }.let {
    it[0as to it[1as B
  }

Theoretice, tali processu parallelo, 200-400 ms habebimus, quod iam prope est ad exspectationem nostram.

Infeliciter, talis parallelisatio bonorum non est, et pretium solvendi satis crudele est: cum paucis simul utentibus operantibus, verberare petitiones in operas incidant, quae in parallelis haud usquam discursum erunt, sic nos. revertemur ad tristia nostra IV s.

Proventus meus cum tali servitio utens est 1300-1700 ms ad 20 epistulas expediendas. Haec in prima exsecutione velocior est, sed tamen quaestionem non solvit.

Alternativus usus queries parallelaeQuid si tertiae factionis officia processus massam non praebent? Exempli gratia, potes occultare defectum batch processus exsecutionis intra modi instrumenti interfaciendi;

interface UserRemoteApi {
  fun getUserById(idUserReference): UserRemote
  fun getUsersByIds(idSet<UserReference>): Set<UserRemote> =
    id.parallelStream()
      .map getUserById(it}.collect(toSet())
  fun getUsersByIds(idList<UserReference>): List<UserRemote> =
    id.parallelStream()
      .map getUserById(it}.collect(toList())
}

Hoc sensu efficitur si massam processus in futuris versionibus videre speras.
pros,

  1. Facile efficiendum nuntium substructio processus parallelus.
  2. Bonam scalam.

cons:

  1. Necessitas data acquirendi ab eius processu separandi, cum petitiones dispensandi ad diversa officia in parallelis.
  2. Auctus onus tertiae factionis officia.

Scopus applicabilitatis idem fere esse potest quod aditus simplices perspici potest. Sensum efficit modum petitionis parallelae utendi, si augere vis observantiam servitutis tuae pluries ob abusionem aliorum inclementium. In exemplo nostro, effectus 2,5 temporibus augetur, sed hoc satis perspicue non est.

caching

Facere potes in spiritu JPA in servitiis externis, hoc est, res receptas intra sessionem thesaurizare ut ea iterum recipias (inclusa in batch processui). Tales thesauros teipsum facere potes, vere uti potes cum suis @Cacheable, plus semper prompto cache facto uti EhCache manually.

Communis quaestio esset, thesauros tantum utiles esse si hits haberent. In nobis, hits in agro auctoris valde probabile est (dicamus, 50), sed nullae hit in lima omnino erunt. Hic accessus aliquas emendationes dabit, sed effectum non funditus mutabit (et erumpere opus est).

Intersessio (longa) cacheria logicam infirmationem implicatam requirunt. In genere, posteriora descendes ad problemata solvenda utentes intersessione cache, melius.

pros,

  1. Effectus caching sine mutatione codice.
  2. Augeri fructibus aliquotiens (in quibusdam).

cons:

  1. Facultas reduci perficiendi si male usus est.
  2. Magnae memoriae caput, praesertim cum longis.
  3. Implicata infirmatio, errores in quibus difficiles ad difficultates reproducendas in temporis spatio perducent.

Saepissime, cella tantum adhibita sunt ut problemata consilio cito repeciare possint. Hoc non debet adhiberi. Sed semper caute eas tractare debes et primum lucrum inde aestimare, et tunc demum iudicium facere.

In exemplo nostro cella incrementum 25% circiter praebebit. Eodem tempore, gazophylacia multa incommoda habent, quibus hic nolui uti.

results

Sic inspeximus simplicem exsecutionem servitii quae batch processui utitur, et quaedam simplices vias ad illud accelerandum.

Praecipua autem horum omnium utilitas est simplicitas, ex qua multae iucundae consequuntur.

Commune problema cum his modis est pauper effectus, praesertim propter magnitudinem facis. Si igitur hae solutiones tibi non conveniunt, tum valet methodus plures radicales considerare.

Duae partes principales sunt in quibus solutiones quaerere potes:

  • opus asynchronum cum notitia (requirit mutationem paradigma, ideo in hoc articulo non tractatur);
  • dilatationem batches servato synchrono processui.

Dilatatio batches numerum vocationum externarum multum imminuet et simul codicem synchronum servabit. Proxima pars articuli huic argumento tradenda erit.

Source: www.habr.com

Add a comment