Mathata a ho sebetsa ha lipotso tsa batch le tharollo ea bona (karolo ea 1)

Mathata a ho sebetsa ha lipotso tsa batch le tharollo ea bona (karolo ea 1)Hoo e ka bang lihlahisoa tsohle tsa morao-rao tsa software li na le litšebeletso tse 'maloa. Hangata, nako e telele ea karabelo ea likanale tsa litšebeletso e fetoha mohloli oa mathata a ts'ebetso. Tharollo e tloaelehileng ea bothata ba mofuta ona ke ho kenya likopo tse ngata tsa litšebeletso ka har'a sephutheloana se le seng, se bitsoang batching.

Haeba u sebelisa ts'ebetso ea batch, u ka 'na ua se ke ua thabela liphello ho latela ts'ebetso kapa ho hlaka ha khoutu. Mokhoa ona ha o bonolo ho motho ea letsetsang kamoo o ka nahanang kateng. Bakeng sa merero e fapaneng le maemong a fapaneng, tharollo e ka fapana haholo. Ho sebelisa mehlala e tobileng, ke tla bontša melemo le boiketlo ba mekhoa e mengata.

Morero oa lipontšo

Bakeng sa ho hlaka, a re shebeng mohlala oa e 'ngoe ea litšebeletso tsa kopo eo ke ntseng ke sebetsa ho eona hajoale.

Tlhaloso ea khetho ea sethala bakeng sa mehlalaBothata ba ts'ebetso e mpe ke bo akaretsang mme ha bo ame lipuo kapa sethala se itseng. Sehlooho sena se tla sebelisa mehlala ea khoutu ea Spring + Kotlin ho bontša mathata le tharollo. Kotlin e utloahala ka mokhoa o ts'oanang (kapa ha e utloisisehe) ho baetsi ba Java le C #; ho feta moo, khoutu e kopane haholoanyane ebile e utloahala ho feta Java. Ho etsa hore lintho li utloisisehe habonolo bakeng sa baetsi ba Java ba hloekileng, ke tla  qoba boloi ba Kotlin, 'me ke sebelise boloi feela (ka moea oa Lombok). Ho tla ba le mekhoa e seng mekae ea katoloso, empa ha e le hantle e tloaelehile ho bohle ba etsang mananeo a Java e le mekhoa e tsitsitseng, kahoo ena e tla ba tsoekere e nyenyane e ke keng ea senya tatso ea sejana.
Ho na le tšebeletso ea tumello ea litokomane. Motho e mong o etsa tokomane ebe o e romela hore ho buisanoe ka eona, nakong eo ho etsoang liphetoho, 'me qetellong ho lumellanoe ka tokomane eo. Tšebeletso ea tumello ka boeona ha e tsebe letho ka litokomane: ke moqoqo feela oa ba amohelehang ba nang le mesebetsi e nyenyane e eketsehileng eo re ke keng ra e nahana mona.

Kahoo, ho na le likamore tsa moqoqo (tse tsamaellanang le litokomane) tse nang le sehlopha se boletsoeng esale pele sa barupeluoa ho e 'ngoe le e 'ngoe ea tsona. Joalo ka lipuisanong tse tloaelehileng, melaetsa e na le mongolo le lifaele mme e ka ba likarabo kapa ho fetisa:

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
)

Lihokelo tsa faele le basebelisi ke lihokelo tsa libaka tse ling. Mona re phela tjena:

typealias FileReference Long
typealias UserReference Long

Lintlha tsa mosebelisi li bolokiloe ho Keycloak mme li amoheloa ka REST. Hoa tšoana le bakeng sa lifaele: lifaele le metainformation mabapi le tsona li lula tšebeletsong e fapaneng ea polokelo ea lifaele.

Mehala eohle ea lits'ebeletso tsena e likopo tse boima. Sena se bolela hore ts'ebetso ea ho tsamaisa likopo tsena e kholo ho feta nako eo e e nkang hore e sebetsoe ke ts'ebeletso ea mokha oa boraro. Libankang tsa rona tsa liteko, nako e tloaelehileng ea mohala bakeng sa lits'ebeletso tse joalo ke 100 ms, kahoo re tla sebelisa linomoro tsena nakong e tlang.

Re hloka ho etsa taolo e bonolo ea REST ho amohela melaetsa ea ho qetela ea N e nang le lintlha tsohle tse hlokahalang. Ke hore, re lumela hore ka pele mokhoa oa molaetsa o batla o tšoana 'me data eohle e lokela ho romeloa. Phapang lipakeng tsa mofuta o ka pele ke hore faele le mosebelisi li hloka ho hlahisoa ka mokhoa o sa hlakoloeng hanyane e le ho li etsa lihokela:

/** В таком виде отдаются ссылки на сущности для фронта */
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
)

Re hloka ho kenya tshebetsong tse latelang:

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

Postfix UI e bolela mefuta ea DTO bakeng sa sebaka se ka pele, ke hore, seo re tlamehang ho se sebeletsa ka REST.

Se ka 'nang sa makatsa mona ke hore ha re fetise sekhetho leha e le sefe sa moqoqo mme esita le ka ChatMessage/ChatMessageUI ea mohlala ha ho na letho. Ke entse sena ka boomo e le hore ke se ke ka kopanya khoutu ea mehlala (lipuisano li arohane, kahoo re ka nahana hore re na le e le 'ngoe feela).

Ho kheloha ha filosofiKa bobeli sehlopha sa ChatMessageUI le mokhoa oa ChatRestApi.getLast li sebelisa mofuta oa data oa List, athe ha e le hantle ke Sete e laetsoeng. Ho JDK sena sohle se mpe, kahoo ho phatlalatsa tatellano ea likarolo boemong ba sebopeho (ho boloka taelo ha u eketsa le ho khutlisa) ho ke ke ha sebetsa. Kahoo e se e le mokhoa o tloaelehileng oa ho sebelisa List maemong ao ho hlokahalang Set se laetsoeng (ho boetse ho na le LinkedHashSet, empa sena ha se sebopeho).
Moeli oa bohlokoa: Re tla nka hore ha ho na liketane tse telele tsa likarabo kapa phetisetso. Ke hore li teng, empa bolelele ba tsona ha bo fete melaetsa e meraro. Letoto lohle la melaetsa le tlameha ho fetisoa ho ea pele.

Ho fumana data ho tsoa lits'ebeletso tsa kantle ho na le li-API tse latelang:

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>
}

Ho ka bonoa hore lits'ebeletso tsa kantle qalong li fana ka ts'ebetso ea li-batch, le ka mefuta e 'meli: ka Set (ntle le ho boloka tatellano ea likarolo, ka linotlolo tse ikhethang) le ka List (ho ka ba le tse kopitsoang - taelo e bolokiloe).

Lisebelisoa tse bonolo

Ts'ebetsong e sa tsebeng letho

Ts'ebetsong ea pele e se nang kelello ea molaoli oa rona oa REST e tla shebahala tjena maemong a mangata:

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()
    )
}

Ntho e 'ngoe le e' ngoe e hlakile haholo, 'me ena ke phaello e kholo.

Re sebelisa batch processing le ho amohela data ho tsoa litšebeletsong tsa kantle ka lihlopha. Empa ho etsahala'ng ka tlhahiso ea rona?

Bakeng sa molaetsa o mong le o mong, mohala o le mong ho UserRemoteApi o tla etsoa ho fumana data tšimong ea mongoli le mohala o le mong ho FileRemoteApi ho fumana lifaele tsohle tse khomaretsoeng. Ho bonahala eka ke eona. Ha re re likarolo tsa forwardFrom le replyTo bakeng sa ChatMessage li fumaneha ka tsela eo sena se sa hlokeng mehala e sa hlokahaleng. Empa ho li fetola ChatMessageUI ho tla lebisa ho ipheta-pheta, ke hore, likhakanyo tsa mehala li ka eketseha haholo. Joalokaha re hlokometse pejana, ha re nke hore ha re na sehlaha se seholo mme ketane e lekanyelitsoe ho melaetsa e meraro.

Ka lebaka leo, re tla fumana mehala e 'meli ho isa ho tse tšeletseng ho ea lits'ebeletso tsa kantle ho molaetsa ka mong le mohala o le mong oa JPA bakeng sa sephutheloana sohle sa melaetsa. Palo eohle ea mehala e tla fapana ho tloha ho 2*N+1 ho isa ho 6*N+1. Ke bokae ho liyuniti tsa 'nete? Ha re re ho hloka melaetsa e 20 ho fana ka leqephe. Ho li fumana, o tla hloka ho tloha ho 4 ho isa ho 10 s. E nyarosang! Ke kopa ho e boloka ka har'a 500ms. 'Me kaha ba ne ba lora ka ho phenya ka mokhoa o se nang moeli karolong e ka pele, litlhoko tsa ts'ebetso bakeng sa ntlha ena li ka imena habeli.

Melemo:

  1. Khoutu e khuts'oane ebile e itokolla (toro ea sehlopha sa ts'ehetso).
  2. Khoutu e bonolo, kahoo ha ho na menyetla ea ho ithunya ka leoto.
  3. Ts'ebetso ea batch ha e shebahale joalo ka ntho e sa tloaelehang 'me e hokahane le mohopolo.
  4. Liphetoho tsa logic li tla ba bonolo ho li etsa 'me li tla ba tsa lehae.

Ho tlosa:

Ts'ebetso e nyarosang ka lebaka la lipakete tse nyane haholo.

Mokhoa ona o ka bonoa hangata litšebeletsong tse bonolo kapa ka li-prototypes. Haeba lebelo la ho etsa liphetoho le bohlokoa, ha ho na thuso ho thatafatsa sistimi. Ka nako e ts'oanang, bakeng sa ts'ebeletso ea rona e bonolo haholo ts'ebetso e tšabeha, kahoo sebaka sa tšebeliso ea mokhoa ona se moqotetsane haholo.

Naive parallel processing

U ka qala ho sebetsana le melaetsa eohle ka tsela e tšoanang - sena se tla u lumella ho tlosa ho eketseha ha nako ka nako ho itšetlehile ka palo ea melaetsa. Ena ha se tsela e ntle haholo hobane e tla fella ka mojaro o moholo oa tlhoro tšebetsong ea kantle.

Ho kenya tšebetsong parallel process ho bonolo haholo:

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

Re sebelisa ts'ebetso ea melaetsa e ts'oanang, re fumana 300-700 ms ka nepo, e leng betere ho feta ts'ebetsong e se nang kelello, empa e ntse e sa potlake ka ho lekaneng.

Ka mokhoa ona, likopo ho userRepository le fileRepository li tla etsoa ka mokhoa o lumellanang, o sa sebetseng hantle. Ho lokisa sena, o tla tlameha ho fetola mohopolo oa mohala haholo. Mohlala, ka 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()!!

Ho ka bonoa hore khoutu e bonolo ea 'mapa ea pele e se e sa utloisisehe. Sena ke hobane re ile ra tlameha ho arola mehala ho lits'ebeletso tsa kantle ho moo liphetho li sebelisoang. Sena ka bosona ha se mpe. Empa ho kopanya mehala ha ho shebahale ka bokhabane haholo mme ho tšoana le "noodle" e tloaelehileng e sebetsang.

Haeba u sebelisa li-coroutines, ntho e 'ngoe le e' ngoe e tla shebahala e hlompheha haholoanyane:

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()
    )
  }

Moo:

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

Ka khopolo, re sebelisa ts'ebetso e tšoanang, re tla fumana 200-400 ms, e seng e le haufi le litebello tsa rona.

Ka bomalimabe, papiso e ntle joalo ha e etsahale, 'me theko ea ho lefa e sehlōhō haholo: ka basebelisi ba fokolang feela ba sebetsang ka nako e le' ngoe, litšebeletso li tla hlaseloa ke likopo tse ngata tse ke keng tsa sebetsoa ka mokhoa o ts'oanang, kahoo re e tla khutlela ho 4s ea rona e bohloko.

Sephetho sa ka ha u sebelisa ts'ebeletso e joalo ke 1300-1700 ms bakeng sa ho sebetsana le melaetsa e 20. Sena se potlakile ho feta ts'ebetsong ea pele, empa leha ho le joalo ha e rarolle bothata.

Tšebeliso e 'ngoe ea lipotso tse tšoanangHo thoe'ng haeba litšebeletso tsa motho oa boraro li sa fane ka ts'ebetso ea batch? Mohlala, o ka pata khaello ea ts'ebetso ea batch ka har'a mekhoa ea li-interface:

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())
}

Sena sea utloahala haeba u ts'epa ho bona ts'ebetso ea batch liphetolelong tse tlang.
Melemo:

  1. Kenya ts'ebetso ea parallel e thehiloeng ho molaetsa habonolo.
  2. scalability e ntle.

Chelete:

  1. Tlhokahalo ea ho arola ho nkuoa ha data ho ts'ebetso ea eona ha ho sebetsa likopo tsa lits'ebeletso tse fapaneng ka ho ts'oana.
  2. Keketseho ea mojaro litšebeletsong tsa batho ba bang.

Hoa hlokomeleha hore sebaka sa ho sebetsa se batla se lekana le sa mokhoa o sa tsebeng letho. Hoa utloahala ho sebelisa mokhoa o tšoanang oa kopo haeba u batla ho eketsa ts'ebetso ea ts'ebeletso ea hau ka makhetlo a 'maloa ka lebaka la tlatlapo e se nang mohau ea ba bang. Mohlala oa rona, tlhahiso e eketsehile ka makhetlo a 2,5, empa ho hlakile hore sena ha sea lekana.

Caching

U ka etsa caching ka moea oa JPA bakeng sa lits'ebeletso tsa kantle, ke hore, boloka lintho tse fumanoeng ka har'a seboka e le hore u se ke oa li fumana hape (ho kenyeletsoa nakong ea ts'ebetso ea batch). U ka iketsetsa li-cache tse joalo, u ka sebelisa Spring ka @Cacheable ea eona, hape u ka sebelisa cache e lokiselitsoeng joalo ka EhCache ka letsoho.

Bothata bo tloaelehileng e ka ba hore li-cache li na le thuso feela haeba li na le li-hits. Tabeng ea rona, ho otla lebaleng la mongoli ho ka etsahala haholo (ha re re, 50%), empa ho ke ke ha e-ba le likotlo ho lifaele ho hang. Mokhoa ona o tla fana ka lintlafatso, empa o ke ke oa fetola tlhahiso haholo (mme re hloka katleho).

Li-cache tsa intersession (bolelele) li hloka mohopolo o rarahaneng oa ho se sebetse. Ka kakaretso, ha u fihla hamorao ho rarolla mathata a ts'ebetso u sebelisa li-cache tsa intersession, ho molemo.

Melemo:

  1. Kenya tšebetsong caching ntle le ho fetola khoutu.
  2. Keketseho ea tlhahiso ka makhetlo a 'maloa (maemong a mang).

Chelete:

  1. Monyetla oa ho fokotsa ts'ebetso haeba e sebelisoa hampe.
  2. Mohopolo o moholo, haholo-holo ka li-cache tse telele.
  3. Ho se sebetse ho rarahaneng, liphoso tseo ho tsona li tla lebisa mathateng a thata ho hlahisa nako ea ho sebetsa.

Hangata, li-cache li sebelisoa feela ho lokisa mathata a moralo kapele. Sena ha se bolele hore ha lia lokela ho sebelisoa. Leha ho le joalo, u lokela ho lula u li tšoara ka hloko 'me u qale ka ho hlahloba phaello e hlahisoang ke ts'ebetso, ebe joale u etsa qeto.

Mohlala oa rona, li-cache li tla fana ka keketseho ea ts'ebetso ea hoo e ka bang 25%. Ka nako e ts'oanang, li-cache li na le mathata a mangata, kahoo nke ke ka li sebelisa mona.

Liphello

Kahoo, re ile ra sheba ts'ebetso e se nang kelello ea ts'ebeletso e sebelisang ts'ebetso ea batch, le mekhoa e 'maloa e bonolo ea ho e potlakisa.

Molemo o ka sehloohong oa mekhoa ena kaofela ke bonolo, moo ho nang le liphello tse ngata tse monate.

Bothata bo tloaelehileng ka mekhoa ena ke ts'ebetso e fokolang, haholo-holo e amanang le boholo ba lipakete. Ka hona, haeba litharollo tsena li sa lumellane le uena, joale ho bohlokoa ho nahana ka mekhoa e matla haholoanyane.

Ho na le litsela tse peli tsa mantlha tseo u ka batlang litharollo ho tsona:

  • mosebetsi oa asynchronous o nang le data (o hloka phetoho ea paradigm, kahoo ha e buuoe sehloohong sena);
  • ho atolosoa ha lihlopha ha u ntse u boloka ts'ebetso ea synchronous.

Katoloso ea lihlopha e tla fokotsa haholo palo ea mehala ea kantle mme ka nako e ts'oanang e boloke khoutu e lumellana. Karolo e latelang ea sehlooho e tla neheloa sehloohong sena.

Source: www.habr.com

Eketsa ka tlhaloso