ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

Или ΠΌΠ°Π»ΠΊΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ½Π° тСтрисология.
Всичко Π½ΠΎΠ²ΠΎ Π΅ Π΄ΠΎΠ±Ρ€Π΅ Π·Π°Π±Ρ€Π°Π²Π΅Π½ΠΎ старо.
Π•ΠΏΠΈΠ³Ρ€Π°Ρ„ΠΈ.
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌ изявлСниС

НСобходимо Π΅ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎ Π΄Π° изтСглятС тСкущия Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS към локалния Linux хост. НС Π² Ρ€Π΅Π°Π»Π½ΠΎ Π²Ρ€Π΅ΠΌΠ΅, Π½ΠΎ, Π΄Π° ΠΊΠ°ΠΆΠ΅ΠΌ, с ΠΌΠ°Π»ΠΊΠΎ закъснСниС.
ΠŸΠ΅Ρ€ΠΈΠΎΠ΄ΡŠΡ‚ Π½Π° изтСглянС Π½Π° актуализацията Π½Π° Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π° Π΅ 5 ΠΌΠΈΠ½ΡƒΡ‚ΠΈ.
РСгистрационният Ρ„Π°ΠΉΠ» Π² AWS сС смСня Π½Π° всСки час.

Използвани инструмСнти

Π—Π° Π΄Π° ΠΊΠ°Ρ‡ΠΈΡ‚Π΅ рСгистрационния Ρ„Π°ΠΉΠ» Π½Π° хоста, сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° bash скрипт, ΠΊΠΎΠΉΡ‚ΠΎ ΠΈΠ·Π²ΠΈΠΊΠ²Π° AWS API "aws rds изтСглянС-db-log-Ρ„Π°ΠΉΠ»-част".

ΠžΠΏΡ†ΠΈΠΈ:

  • --db-instance-identifier: ΠΈΠΌΠ΅ Π½Π° СкзСмпляр Π² AWS;
  • --log-file-name: ΠΈΠΌΠ΅ Π½Π° Ρ‚Π΅ΠΊΡƒΡ‰ΠΎ гСнСрирания Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»
  • --max-item: ΠžΠ±Ρ‰ΠΈΡΡ‚ Π±Ρ€ΠΎΠΉ Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚ΠΈ, Π²ΡŠΡ€Π½Π°Ρ‚ΠΈ Π² ΠΈΠ·Ρ…ΠΎΠ΄Π° Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°Ρ‚Π°.Π Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ Π½Π° ΠΏΠ°Ρ€Ρ‡Π΅Ρ‚ΠΎ Π½Π° изтСглСния Ρ„Π°ΠΉΠ».
  • --starting-token: Π‘Ρ‚Π°Ρ€Ρ‚ΠΎΠ² Ρ‚ΠΎΠΊΠ΅Π½ Ρ‚ΠΎΠΊΠ΅Π½

Π’ Ρ‚ΠΎΠ·ΠΈ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π΅Π½ случай Π·Π°Π΄Π°Ρ‡Π°Ρ‚Π° Π·Π° изтСглянС Π½Π° рСгистрационни Ρ„Π°ΠΉΠ»ΠΎΠ²Π΅ възникна Π² Ρ…ΠΎΠ΄Π° Π½Π° Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚Π° ΠΏΠΎ ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ Π½Π° производитСлността Π½Π° PostgreSQL заявки.

Π”Π°, ΠΈ просто - интСрСсна Π·Π°Π΄Π°Ρ‡Π° Π·Π° ΠΎΠ±ΡƒΡ‡Π΅Π½ΠΈΠ΅ ΠΈ Ρ€Π°Π·Π½ΠΎΠΎΠ±Ρ€Π°Π·ΠΈΠ΅ Π² Ρ€Π°Π±ΠΎΡ‚Π½ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅.
ΠŸΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°ΠΌ, Ρ‡Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡŠΡ‚ Π²Π΅Ρ‡Π΅ Π΅ Ρ€Π΅ΡˆΠ΅Π½ ΠΏΠΎ силата Π½Π° Ρ€ΡƒΡ‚ΠΈΠ½Π°. Но Π±ΡŠΡ€Π·ΠΈΡΡ‚ Google Π½Π΅ ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠΈ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ ΠΈ нямашС особСно ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ Π·Π° ΠΏΠΎ-Π·Π°Π΄ΡŠΠ»Π±ΠΎΡ‡Π΅Π½ΠΎ Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅. Π’ΡŠΠ² всСки случай Ρ‚ΠΎΠ²Π° Π΅ Π΄ΠΎΠ±Ρ€Π° Ρ‚Ρ€Π΅Π½ΠΈΡ€ΠΎΠ²ΠΊΠ°.

Π€ΠΎΡ€ΠΌΠ°Π»ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° Π·Π°Π΄Π°Ρ‡Π°Ρ‚Π°

ΠšΡ€Π°ΠΉΠ½ΠΈΡΡ‚ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» Π΅ Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ Ρ€Π΅Π΄ΠΎΠ²Π΅ с ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π° дълТина. Π“Ρ€Π°Ρ„ΠΈΡ‡Π½ΠΎ рСгистрационният Ρ„Π°ΠΉΠ» ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС прСдставСн ΠΏΠΎ слСдния Π½Π°Ρ‡ΠΈΠ½:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

Напомня Π»ΠΈ Π²ΠΈ Π²Π΅Ρ‡Π΅ Π½Π΅Ρ‰ΠΎ? Какво става с "тСтрис"? И Π΅Ρ‚ΠΎ ΠΊΠ°ΠΊΠ²ΠΎ.
Ако прСдставим Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΈΡ‚Π΅ ΠΎΠΏΡ†ΠΈΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ Π²ΡŠΠ·Π½ΠΈΠΊΠ²Π°Ρ‚ ΠΏΡ€ΠΈ Π·Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅Ρ‚ΠΎ Π½Π° слСдващия Ρ„Π°ΠΉΠ» Π³Ρ€Π°Ρ„ΠΈΡ‡Π½ΠΎ (Π·Π° простота, Π² Ρ‚ΠΎΠ·ΠΈ случай Π½Π΅ΠΊΠ° Ρ€Π΅Π΄ΠΎΠ²Π΅Ρ‚Π΅ ΠΈΠΌΠ°Ρ‚ Π΅Π΄Π½Π°ΠΊΠ²Π° дълТина), ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ стандартни тСтрис Ρ„ΠΈΠ³ΡƒΡ€ΠΈ:

1) Π€Π°ΠΉΠ»ΡŠΡ‚ Π΅ ΠΈΠ·Ρ‚Π΅Π³Π»Π΅Π½ изцяло ΠΈ Π΅ ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»Π΅Π½. Π Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ Π½Π° частта Π΅ ΠΏΠΎ-голям ΠΎΡ‚ крайния Ρ€Π°Π·ΠΌΠ΅Ρ€ Π½Π° Ρ„Π°ΠΉΠ»Π°:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

2) Π€Π°ΠΉΠ»ΡŠΡ‚ ΠΈΠΌΠ° ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ΅Π½ΠΈΠ΅. Π Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ Π½Π° частта Π΅ ΠΏΠΎ-малък ΠΎΡ‚ крайния Ρ€Π°Π·ΠΌΠ΅Ρ€ Π½Π° Ρ„Π°ΠΉΠ»Π°:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

3) Π€Π°ΠΉΠ»ΡŠΡ‚ Π΅ ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½ΠΈΡ Ρ„Π°ΠΉΠ» ΠΈ ΠΈΠΌΠ° ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ΅Π½ΠΈΠ΅. Π Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ Π½Π° ΠΏΠ°Ρ€Ρ‡Π΅Ρ‚ΠΎ Π΅ ΠΏΠΎ-малък ΠΎΡ‚ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° останалата част ΠΎΡ‚ крайния Ρ„Π°ΠΉΠ»:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

4) Π€Π°ΠΉΠ»ΡŠΡ‚ Π΅ ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ΅Π½ΠΈΠ΅ Π½Π° ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½ΠΈΡ Ρ„Π°ΠΉΠ» ΠΈ Π΅ ΠΎΠΊΠΎΠ½Ρ‡Π°Ρ‚Π΅Π»Π΅Π½. Π Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ Π½Π° ΠΏΠ°Ρ€Ρ‡Π΅Ρ‚ΠΎ Π΅ ΠΏΠΎ-голям ΠΎΡ‚ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° останалата част ΠΎΡ‚ крайния Ρ„Π°ΠΉΠ»:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

Π—Π°Π΄Π°Ρ‡Π°Ρ‚Π° Π΅ Π΄Π° сглобитС ΠΏΡ€Π°Π²ΠΎΡŠΠ³ΡŠΠ»Π½ΠΈΠΊ ΠΈΠ»ΠΈ Π΄Π° ΠΈΠ³Ρ€Π°Π΅Ρ‚Π΅ Tetris Π½Π° Π½ΠΎΠ²ΠΎ Π½ΠΈΠ²ΠΎ.
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ Π²ΡŠΠ·Π½ΠΈΠΊΠ²Π°Ρ‚ Π² Ρ…ΠΎΠ΄Π° Π½Π° Ρ€Π΅ΡˆΠ°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°

1) Π—Π°Π»Π΅ΠΏΠ΅Ρ‚Π΅ Π½ΠΈΠ· ΠΎΡ‚ 2 части

ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS
ΠšΠ°Ρ‚ΠΎ цяло нямашС особСни ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ. Π‘Ρ‚Π°Π½Π΄Π°Ρ€Ρ‚Π½Π° Π·Π°Π΄Π°Ρ‡Π° ΠΎΡ‚ Π½Π°Ρ‡Π°Π»Π΅Π½ курс ΠΏΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈΡ€Π°Π½Π΅.

ΠžΠΏΡ‚ΠΈΠΌΠ°Π»Π΅Π½ Ρ€Π°Π·ΠΌΠ΅Ρ€ Π½Π° порцията

Но Ρ‚ΠΎΠ²Π° Π΅ ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎ-интСрСсно.
Π—Π° съТалСниС няма Π½Π°Ρ‡ΠΈΠ½ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° отмСстванС слСд Π΅Ρ‚ΠΈΠΊΠ΅Ρ‚Π° Π½Π° Π½Π°Ρ‡Π°Π»Π½Π°Ρ‚Π° част:

ΠšΠ°ΠΊΡ‚ΠΎ Π²Π΅Ρ‡Π΅ Π·Π½Π°Π΅Ρ‚Π΅, опцията --starting-token сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π·Π° ΡƒΠΊΠ°Π·Π²Π°Π½Π΅ ΠΎΡ‚ΠΊΡŠΠ΄Π΅ Π΄Π° Π·Π°ΠΏΠΎΡ‡Π½Π΅ ΠΏΠ°Π³ΠΈΠ½ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ. Π’Π°Π·ΠΈ опция ΠΏΡ€ΠΈΠ΅ΠΌΠ° стойности Π½Π° Π½ΠΈΠ·, ΠΊΠΎΠ΅Ρ‚ΠΎ Π±ΠΈ ΠΎΠ·Π½Π°Ρ‡Π°Π²Π°Π»ΠΎ, Ρ‡Π΅ Π°ΠΊΠΎ сС ΠΎΠΏΠΈΡ‚Π°Ρ‚Π΅ Π΄Π° Π΄ΠΎΠ±Π°Π²ΠΈΡ‚Π΅ стойност Π½Π° отмСстванС ΠΏΡ€Π΅Π΄ Π½ΠΈΠ·Π° Π½Π° слСдващия Ρ‚ΠΎΠΊΠ΅Π½, опцията няма Π΄Π° бъдС Π²Π·Π΅Ρ‚Π° ΠΏΠΎΠ΄ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ ΠΊΠ°Ρ‚ΠΎ отмСстванС.

И Ρ‚Π°ΠΊΠ°, трябва Π΄Π° Ρ‡Π΅Ρ‚Π΅Ρ‚Π΅ Π½Π° ΠΏΠ°Ρ€Ρ‡Π΅Ρ‚Π°-ΠΏΠΎΡ€Ρ†ΠΈΠΈ.
Ако Ρ‡Π΅Ρ‚Π΅Ρ‚Π΅ Π½Π° Π³ΠΎΠ»Π΅ΠΌΠΈ ΠΏΠΎΡ€Ρ†ΠΈΠΈ, Ρ‚ΠΎΠ³Π°Π²Π° броят Π½Π° чСтСнията Ρ‰Π΅ бъдС ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π΅Π½, Π½ΠΎ ΠΎΠ±Π΅ΠΌΡŠΡ‚ Ρ‰Π΅ бъдС максималСн.
Ако Ρ‡Π΅Ρ‚Π΅Ρ‚Π΅ Π½Π° ΠΌΠ°Π»ΠΊΠΈ ΠΏΠΎΡ€Ρ†ΠΈΠΈ, Ρ‚ΠΎΠ³Π°Π²Π°, Π½Π°ΠΏΡ€ΠΎΡ‚ΠΈΠ², броят Π½Π° чСтСнията Ρ‰Π΅ бъдС максималСн, Π½ΠΎ ΠΎΠ±Π΅ΠΌΡŠΡ‚ Ρ‰Π΅ бъдС ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π΅Π½.
Π•Ρ‚ΠΎ Π·Π°Ρ‰ΠΎ, Π·Π° Π΄Π° намаля Ρ‚Ρ€Π°Ρ„ΠΈΠΊΠ° ΠΈ Π·Π° цялостната красота Π½Π° Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ, Ρ‚Ρ€ΡΠ±Π²Π°ΡˆΠ΅ Π΄Π° измисля някакво Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅, ΠΊΠΎΠ΅Ρ‚ΠΎ, Π·Π° съТалСниС, ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΌΠ°Π»ΠΊΠΎ ΠΊΠ°Ρ‚ΠΎ ΠΏΠ°Ρ‚Π΅Ρ€ΠΈΡ†Π°.

Π—Π° Π΄Π° ΠΈΠ»ΡŽΡΡ‚Ρ€ΠΈΡ€Π°ΠΌΠ΅, Π½Π΅ΠΊΠ° Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ процСса Π½Π° изтСглянС Π½Π° Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» Π² 2 силно опростСни вСрсии. Броят Π½Π° показанията ΠΈ Π² Π΄Π²Π°Ρ‚Π° случая зависи ΠΎΡ‚ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° порцията.

1) Π—Π°Ρ€Π΅Π΄Π΅Ρ‚Π΅ Π½Π° ΠΌΠ°Π»ΠΊΠΈ ΠΏΠΎΡ€Ρ†ΠΈΠΈ:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

2) Π—Π°Ρ€Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° Π³ΠΎΠ»Π΅ΠΌΠΈ ΠΏΠΎΡ€Ρ†ΠΈΠΈ:
ΠšΠ°Ρ‡Π²Π°Π½Π΅ Π½Π° ΠΆΡƒΡ€Π½Π°Π» Π½Π° PostgreSQL ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ° Π½Π° AWS

ΠšΠ°ΠΊΡ‚ΠΎ ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ΠΎ, ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»Π½ΠΎΡ‚ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ Π΅ ΠΏΠΎ срСдата.
Π Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ Π½Π° порцията Π΅ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π΅Π½, Π½ΠΎ Π² процСса Π½Π° Ρ‡Π΅Ρ‚Π΅Π½Π΅ Ρ€Π°Π·ΠΌΠ΅Ρ€ΡŠΡ‚ ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈ, Π·Π° Π΄Π° сС Π½Π°ΠΌΠ°Π»ΠΈ броят Π½Π° чСтСнията.

Врябва Π΄Π° сС ΠΎΡ‚Π±Π΅Π»Π΅ΠΆΠΈ Ρ‡Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡŠΡ‚ с ΠΈΠ·Π±ΠΎΡ€Π° Π½Π° ΠΎΠΏΡ‚ΠΈΠΌΠ°Π»Π΅Π½ Ρ€Π°Π·ΠΌΠ΅Ρ€ Π½Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Π½Π°Ρ‚Π° част всС ΠΎΡ‰Π΅ Π½Π΅ Π΅ напълно Ρ€Π΅ΡˆΠ΅Π½ ΠΈ изисква ΠΏΠΎ-Π·Π°Π΄ΡŠΠ»Π±ΠΎΡ‡Π΅Π½ΠΎ ΠΏΡ€ΠΎΡƒΡ‡Π²Π°Π½Π΅ ΠΈ Π°Π½Π°Π»ΠΈΠ·. МоТС Π±ΠΈ ΠΌΠ°Π»ΠΊΠΎ ΠΏΠΎ-късно.

ΠžΠ±Ρ‰ΠΎ описаниС Π½Π° ΠΈΠ·ΠΏΡŠΠ»Π½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ

Използвани сСрвизни маси

CREATE TABLE endpoint
(
id SERIAL ,
host text 
);

TABLE database
(
id SERIAL , 
…
last_aws_log_time text ,
last_aws_nexttoken text ,
aws_max_item_size integer 
);
last_aws_log_time β€” врСмСнная ΠΌΠ΅Ρ‚ΠΊΠ° послСднСго Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ Π»ΠΎΠ³-Ρ„Π°ΠΉΠ»Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ YYYY-MM-DD-HH24.
last_aws_nexttoken β€” тСкстовая ΠΌΠ΅Ρ‚ΠΊΠ° послСднСй Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½ΠΎΠΉ ΠΏΠΎΡ€Ρ†ΠΈΠΈ.
aws_max_item_size- эмпиричСским ΠΏΡƒΡ‚Π΅ΠΌ, ΠΏΠΎΠ΄ΠΎΠ±Ρ€Π°Π½Π½Ρ‹ΠΉ Π½Π°Ρ‡Π°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ ΠΏΠΎΡ€Ρ†ΠΈΠΈ.

ПълСн тСкст Π½Π° сцСнария

download_aws_piece.sh

#!/bin/bash
#########################################################
# download_aws_piece.sh
# downloan piece of log from AWS
# version HABR
 let min_item_size=1024
 let max_item_size=1048576
 let growth_factor=3
 let growth_counter=1
 let growth_counter_max=3

 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:''STARTED'
 
 AWS_LOG_TIME=$1
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:AWS_LOG_TIME='$AWS_LOG_TIME
  
 database_id=$2
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:database_id='$database_id
 RESULT_FILE=$3 
  
 endpoint=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE_DATABASE -A -t -c "select e.host from endpoint e join database d on e.id = d.endpoint_id where d.id = $database_id "`
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:endpoint='$endpoint
  
 db_instance=`echo $endpoint | awk -F"." '{print toupper($1)}'`
 
 echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:db_instance='$db_instance

 LOG_FILE=$RESULT_FILE'.tmp_log'
 TMP_FILE=$LOG_FILE'.tmp'
 TMP_MIDDLE=$LOG_FILE'.tmp_mid'  
 TMP_MIDDLE2=$LOG_FILE'.tmp_mid2'  
  
 current_aws_log_time=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -c "select last_aws_log_time from database where id = $database_id "`

 echo $(date +%Y%m%d%H%M)':      download_aws_piece.sh:current_aws_log_time='$current_aws_log_time
  
  if [[ $current_aws_log_time != $AWS_LOG_TIME  ]];
  then
    is_new_log='1'
	if ! psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -q -c "update database set last_aws_log_time = '$AWS_LOG_TIME' where id = $database_id "
	then
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - update database set last_aws_log_time .'
	  exit 1
	fi
  else
    is_new_log='0'
  fi
  
  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:is_new_log='$is_new_log
  
  let last_aws_max_item_size=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -c "select aws_max_item_size from database where id = $database_id "`
  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: last_aws_max_item_size='$last_aws_max_item_size
  
  let count=1
  if [[ $is_new_log == '1' ]];
  then    
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: START DOWNLOADING OF NEW AWS LOG'
	if ! aws rds download-db-log-file-portion 
		--max-items $last_aws_max_item_size 
		--region REGION 
		--db-instance-identifier  $db_instance 
		--log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 2
	fi  	
  else
    next_token=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -c "select last_aws_nexttoken from database where id = $database_id "`
	
	if [[ $next_token == '' ]];
	then
	  next_token='0'	  
	fi
	
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: CONTINUE DOWNLOADING OF AWS LOG'
	if ! aws rds download-db-log-file-portion 
	    --max-items $last_aws_max_item_size 
		--starting-token $next_token 
		--region REGION 
		--db-instance-identifier  $db_instance 
		--log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 3
	fi       
	
	line_count=`cat  $LOG_FILE | wc -l`
	let lines=$line_count-1
	  
	tail -$lines $LOG_FILE > $TMP_MIDDLE 
	mv -f $TMP_MIDDLE $LOG_FILE
  fi
  
  next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
  next_token=`echo $next_token_str | awk -F" " '{ print $2}' `
  
  grep -v NEXTTOKEN $LOG_FILE  > $TMP_FILE 
  
  if [[ $next_token == '' ]];
  then
	  cp $TMP_FILE $RESULT_FILE
	  
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:  NEXTTOKEN NOT FOUND - FINISH '
	  rm $LOG_FILE 
	  rm $TMP_FILE
	  rm $TMP_MIDDLE
          rm $TMP_MIDDLE2	  
	  exit 0  
  else
	psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -q -c "update database set last_aws_nexttoken = '$next_token' where id = $database_id "
  fi
  
  first_str=`tail -1 $TMP_FILE`
  
  line_count=`cat  $TMP_FILE | wc -l`
  let lines=$line_count-1    
  
  head -$lines $TMP_FILE  > $RESULT_FILE

###############################################
# MAIN CIRCLE
  let count=2
  while [[ $next_token != '' ]];
  do 
    echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: count='$count
	
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: START DOWNLOADING OF AWS LOG'
	if ! aws rds download-db-log-file-portion 
             --max-items $last_aws_max_item_size 
             --starting-token $next_token 
             --region REGION 
             --db-instance-identifier  $db_instance 
             --log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 4
	fi

	next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
	next_token=`echo $next_token_str | awk -F" " '{ print $2}' `

	TMP_FILE=$LOG_FILE'.tmp'
	grep -v NEXTTOKEN $LOG_FILE  > $TMP_FILE  
	
	last_str=`head -1 $TMP_FILE`
  
    if [[ $next_token == '' ]];
	then
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  tail -$lines $TMP_FILE >> $RESULT_FILE
	  
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:  NEXTTOKEN NOT FOUND - FINISH '
	  rm $LOG_FILE 
	  rm $TMP_FILE
	  rm $TMP_MIDDLE
          rm $TMP_MIDDLE2	  
	  exit 0  
	fi
	
    if [[ $next_token != '' ]];
	then
		let growth_counter=$growth_counter+1
		if [[ $growth_counter -gt $growth_counter_max ]];
		then
			let last_aws_max_item_size=$last_aws_max_item_size*$growth_factor
			let growth_counter=1
		fi
	
		if [[ $last_aws_max_item_size -gt $max_item_size ]]; 
		then
			let last_aws_max_item_size=$max_item_size
		fi 

	  psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -q -c "update database set last_aws_nexttoken = '$next_token' where id = $database_id "
	  
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  #############################
	  #Get middle of file
	  head -$lines $TMP_FILE > $TMP_MIDDLE
	  
	  line_count=`cat  $TMP_MIDDLE | wc -l`
	  let lines=$line_count-1
	  tail -$lines $TMP_MIDDLE > $TMP_MIDDLE2
	  
	  cat $TMP_MIDDLE2 >> $RESULT_FILE	  
	  
	  first_str=`tail -1 $TMP_FILE`	  
	fi
	  
    let count=$count+1

  done
#
#################################################################

exit 0  

Π€Ρ€Π°Π³ΠΌΠ΅Π½Ρ‚ΠΈ ΠΎΡ‚ скрипт с някои обяснСния:

Π’Ρ…ΠΎΠ΄Π½ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ Π½Π° скрипта:

  • Π’Ρ€Π΅ΠΌΠ΅Π²ΠΎ ΠΊΠ»Π΅ΠΉΠΌΠΎ Π½Π° ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° рСгистрационния Ρ„Π°ΠΉΠ» във Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ Π“Π“Π“Π“-ММ-Π”Π”-Π§Π§24: AWS_LOG_TIME=$1
  • ID Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ: database_id=$2
  • ИмС Π½Π° ΡΡŠΠ±Ρ€Π°Π½ΠΈΡ рСгистрационСн Ρ„Π°ΠΉΠ»: RESULT_FILE=$3

Π’Π·Π΅ΠΌΠ΅Ρ‚Π΅ ΠΊΠ»Π΅ΠΉΠΌΠΎΡ‚ΠΎ Π·Π° Π²Ρ€Π΅ΠΌΠ΅ Π½Π° послСдния ΠΊΠ°Ρ‡Π΅Π½ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»:

current_aws_log_time=`psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -c "select last_aws_log_time from database where id = $database_id "`

Ако ΠΊΠ»Π΅ΠΉΠΌΠΎΡ‚ΠΎ Π½Π° послСдния Π·Π°Ρ€Π΅Π΄Π΅Π½ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» Π½Π΅ съвпада с входния ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚ΡŠΡ€, сС Π·Π°Ρ€Π΅ΠΆΠ΄Π° Π½ΠΎΠ² Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»:

if [[ $current_aws_log_time != $AWS_LOG_TIME  ]];
  then
    is_new_log='1'
	if ! psql -h ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -v ON_ERROR_STOP=1 -A -t -c "update database set last_aws_log_time = '$AWS_LOG_TIME' where id = $database_id "
	then
	  echo '***download_aws_piece.sh -FATAL_ERROR - update database set last_aws_log_time .'
	  exit 1
	fi
  else
    is_new_log='0'
  fi

ΠŸΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ стойността Π½Π° Π΅Ρ‚ΠΈΠΊΠ΅Ρ‚Π° nexttoken ΠΎΡ‚ зарСдСния Ρ„Π°ΠΉΠ»:

  next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
  next_token=`echo $next_token_str | awk -F" " '{ print $2}' `

Π—Π½Π°ΠΊΡŠΡ‚ Π·Π° ΠΊΡ€Π°ΠΉ Π½Π° изтСглянСто Π΅ ΠΏΡ€Π°Π·Π½Π°Ρ‚Π° стойност Π½Π° nexttoken.

Π’ Ρ†ΠΈΠΊΡŠΠ» Π½ΠΈΠ΅ Π±Ρ€ΠΎΠΈΠΌ части ΠΎΡ‚ Ρ„Π°ΠΉΠ»Π°, ΠΏΠΎ ΠΏΡŠΡ‚Ρ, ΡΠ²ΡŠΡ€Π·Π²Π°ΠΉΠΊΠΈ Ρ€Π΅Π΄ΠΎΠ²Π΅ ΠΈ ΡƒΠ²Π΅Π»ΠΈΡ‡Π°Π²Π°ΠΉΠΊΠΈ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° частта:
Π“Π»Π°Π²Π΅Π½ ΠΊΠΎΠ½Ρ‚ΡƒΡ€

# MAIN CIRCLE
  let count=2
  while [[ $next_token != '' ]];
  do 
    echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: count='$count
	
	echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: START DOWNLOADING OF AWS LOG'
	if ! aws rds download-db-log-file-portion 
     --max-items $last_aws_max_item_size 
	 --starting-token $next_token 
     --region REGION 
     --db-instance-identifier  $db_instance 
     --log-file-name error/postgresql.log.$AWS_LOG_TIME > $LOG_FILE
	then
		echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh: FATAL_ERROR - Could not get log from AWS .'
		exit 4
	fi

	next_token_str=`cat $LOG_FILE | grep NEXTTOKEN` 
	next_token=`echo $next_token_str | awk -F" " '{ print $2}' `

	TMP_FILE=$LOG_FILE'.tmp'
	grep -v NEXTTOKEN $LOG_FILE  > $TMP_FILE  
	
	last_str=`head -1 $TMP_FILE`
  
    if [[ $next_token == '' ]];
	then
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  tail -$lines $TMP_FILE >> $RESULT_FILE
	  
	  echo $(date +%Y%m%d%H%M)':    download_aws_piece.sh:  NEXTTOKEN NOT FOUND - FINISH '
	  rm $LOG_FILE 
	  rm $TMP_FILE
	  rm $TMP_MIDDLE
         rm $TMP_MIDDLE2	  
	  exit 0  
	fi
	
    if [[ $next_token != '' ]];
	then
		let growth_counter=$growth_counter+1
		if [[ $growth_counter -gt $growth_counter_max ]];
		then
			let last_aws_max_item_size=$last_aws_max_item_size*$growth_factor
			let growth_counter=1
		fi
	
		if [[ $last_aws_max_item_size -gt $max_item_size ]]; 
		then
			let last_aws_max_item_size=$max_item_size
		fi 

	  psql -h MONITOR_ENDPOINT.rds.amazonaws.com -U USER -d MONITOR_DATABASE -A -t -q -c "update database set last_aws_nexttoken = '$next_token' where id = $database_id "
	  
	  concat_str=$first_str$last_str
	  	  
	  echo $concat_str >> $RESULT_FILE
		 
	  line_count=`cat  $TMP_FILE | wc -l`
	  let lines=$line_count-1
	  
	  #############################
	  #Get middle of file
	  head -$lines $TMP_FILE > $TMP_MIDDLE
	  
	  line_count=`cat  $TMP_MIDDLE | wc -l`
	  let lines=$line_count-1
	  tail -$lines $TMP_MIDDLE > $TMP_MIDDLE2
	  
	  cat $TMP_MIDDLE2 >> $RESULT_FILE	  
	  
	  first_str=`tail -1 $TMP_FILE`	  
	fi
	  
    let count=$count+1

  done

Какво слСдва?

И Ρ‚Π°ΠΊΠ°, ΠΏΡŠΡ€Π²Π°Ρ‚Π° ΠΌΠ΅ΠΆΠ΄ΠΈΠ½Π½Π° Π·Π°Π΄Π°Ρ‡Π° - "изтСглянС Π½Π° Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π° ΠΎΡ‚ ΠΎΠ±Π»Π°ΠΊΠ°" Π΅ Ρ€Π΅ΡˆΠ΅Π½Π°. Какво Π΄Π° правя с изтСглСния Π΄Π½Π΅Π²Π½ΠΈΠΊ?
ΠŸΡŠΡ€Π²ΠΎ трябва Π΄Π° Π°Π½Π°Π»ΠΈΠ·ΠΈΡ€Π°Ρ‚Π΅ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π° ΠΈ Π΄Π° ΠΈΠ·Π²Π»Π΅Ρ‡Π΅Ρ‚Π΅ дСйствитСлнитС заявки ΠΎΡ‚ Π½Π΅Π³ΠΎ.
Π—Π°Π΄Π°Ρ‡Π°Ρ‚Π° Π½Π΅ Π΅ ΠΌΠ½ΠΎΠ³ΠΎ Ρ‚Ρ€ΡƒΠ΄Π½Π°. Най-простият bash-скрипт сС справя Π΄ΠΎΠ±Ρ€Π΅.
upload_log_query.sh

#!/bin/bash
#########################################################
# upload_log_query.sh
# Upload table table from dowloaded aws file 
# version HABR
###########################################################  
echo 'TIMESTAMP:'$(date +%c)' Upload log_query table '
source_file=$1
echo 'source_file='$source_file
database_id=$2
echo 'database_id='$database_id

beginer=' '
first_line='1'
let "line_count=0"
sql_line=' '
sql_flag=' '    
space=' '
cat $source_file | while read line
do
  line="$space$line"

  if [[ $first_line == "1" ]]; then
    beginer=`echo $line | awk -F" " '{ print $1}' `
    first_line='0'
  fi

  current_beginer=`echo $line | awk -F" " '{ print $1}' `

  if [[ $current_beginer == $beginer ]]; then
    if [[ $sql_flag == '1' ]]; then
     sql_flag='0' 
     log_date=`echo $sql_line | awk -F" " '{ print $1}' `
     log_time=`echo $sql_line | awk -F" " '{ print $2}' `
     duration=`echo $sql_line | awk -F" " '{ print $5}' `

     #replace ' to ''
     sql_modline=`echo "$sql_line" | sed 's/'''/''''''/g'`
     sql_line=' '

	 ################
	 #PROCESSING OF THE SQL-SELECT IS HERE
     if ! psql -h ENDPOINT.rds.amazonaws.com -U USER -d DATABASE -v ON_ERROR_STOP=1 -A -t -c "select log_query('$ip_port',$database_id , '$log_date' , '$log_time' , '$duration' , '$sql_modline' )" 
     then
        echo 'FATAL_ERROR - log_query '
        exit 1
     fi
	 ################

    fi #if [[ $sql_flag == '1' ]]; then

    let "line_count=line_count+1"

    check=`echo $line | awk -F" " '{ print $8}' `
    check_sql=${check^^}    

    #echo 'check_sql='$check_sql
    
    if [[ $check_sql == 'SELECT' ]]; then
     sql_flag='1'    
     sql_line="$sql_line$line"
	 ip_port=`echo $sql_line | awk -F":" '{ print $4}' `
    fi
  else       

    if [[ $sql_flag == '1' ]]; then
      sql_line="$sql_line$line"
    fi   
    
  fi #if [[ $current_beginer == $beginer ]]; then

done

Π‘Π΅Π³Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Ρ€Π°Π±ΠΎΡ‚ΠΈΡ‚Π΅ със заявката, ΠΈΠ·Π²Π»Π΅Ρ‡Π΅Π½Π° ΠΎΡ‚ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π°.

И ΠΈΠΌΠ° няколко ΠΏΠΎΠ»Π΅Π·Π½ΠΈ Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΠΈ.

АнализиранитС заявки трябва Π΄Π° сС ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π°Ρ‚ някъдС. Π—Π° Ρ‚ΠΎΠ²Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° сСрвизна маса. log_query

CREATE TABLE log_query
(
   id SERIAL ,
   queryid bigint ,
   query_md5hash text not null ,
   database_id integer not null ,  
   timepoint timestamp without time zone not null,
   duration double precision not null ,
   query text not null ,
   explained_plan text[],
   plan_md5hash text  , 
   explained_plan_wo_costs text[],
   plan_hash_value text  ,
   baseline_id integer ,
   ip text ,
   port text 
);
ALTER TABLE log_query ADD PRIMARY KEY (id);
ALTER TABLE log_query ADD CONSTRAINT queryid_timepoint_unique_key UNIQUE (queryid, timepoint );
ALTER TABLE log_query ADD CONSTRAINT query_md5hash_timepoint_unique_key UNIQUE (query_md5hash, timepoint );

CREATE INDEX log_query_timepoint_idx ON log_query (timepoint);
CREATE INDEX log_query_queryid_idx ON log_query (queryid);
ALTER TABLE log_query ADD CONSTRAINT database_id_fk FOREIGN KEY (database_id) REFERENCES database (id) ON DELETE CASCADE ;

Анализираната заявка сС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π° Π² plpgsql Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ "log_query".
log_query.sql

--log_query.sql
--verison HABR
CREATE OR REPLACE FUNCTION log_query( ip_port text ,log_database_id integer , log_date text , log_time text , duration text , sql_line text   ) RETURNS boolean AS $$
DECLARE
  result boolean ;
  log_timepoint timestamp without time zone ;
  log_duration double precision ; 
  pos integer ;
  log_query text ;
  activity_string text ;
  log_md5hash text ;
  log_explain_plan text[] ;
  
  log_planhash text ;
  log_plan_wo_costs text[] ; 
  
  database_rec record ;
  
  pg_stat_query text ; 
  test_log_query text ;
  log_query_rec record;
  found_flag boolean;
  
  pg_stat_history_rec record ;
  port_start integer ;
  port_end integer ;
  client_ip text ;
  client_port text ;
  log_queryid bigint ;
  log_query_text text ;
  pg_stat_query_text text ; 
BEGIN
  result = TRUE ;

  RAISE NOTICE '***log_query';
  
  port_start = position('(' in ip_port);
  port_end = position(')' in ip_port);
  client_ip = substring( ip_port from 1 for port_start-1 );
  client_port = substring( ip_port from port_start+1 for port_end-port_start-1 );

  SELECT e.host , d.name , d.owner_pwd 
  INTO database_rec
  FROM database d JOIN endpoint e ON e.id = d.endpoint_id
  WHERE d.id = log_database_id ;
  
  log_timepoint = to_timestamp(log_date||' '||log_time,'YYYY-MM-DD HH24-MI-SS');
  log_duration = duration:: double precision; 

  
  pos = position ('SELECT' in UPPER(sql_line) );
  log_query = substring( sql_line from pos for LENGTH(sql_line));
  log_query = regexp_replace(log_query,' +',' ','g');
  log_query = regexp_replace(log_query,';+','','g');
  log_query = trim(trailing ' ' from log_query);
 

  log_md5hash = md5( log_query::text );
  
  --Explain execution plan--
  EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||database_rec.host||' dbname='||database_rec.name||' user=DATABASE password='||database_rec.owner_pwd||' '')'; 
  
  log_explain_plan = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN '||log_query ) AS t (plan text) );
  log_plan_wo_costs = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN ( COSTS FALSE ) '||log_query ) AS t (plan text) );
    
  PERFORM dblink_disconnect('LINK1');
  --------------------------
  BEGIN
	INSERT INTO log_query
	(
		query_md5hash ,
		database_id , 
		timepoint ,
		duration ,
		query ,
		explained_plan ,
		plan_md5hash , 
		explained_plan_wo_costs , 
		plan_hash_value , 
		ip , 
		port
	) 
	VALUES 
	(
		log_md5hash ,
		log_database_id , 
		log_timepoint , 
		log_duration , 
		log_query ,
		log_explain_plan , 
		md5(log_explain_plan::text) ,
		log_plan_wo_costs , 
		md5(log_plan_wo_costs::text),
		client_ip , 
		client_port		
	);
	activity_string = 	'New query has logged '||
						' database_id = '|| log_database_id ||
						' query_md5hash='||log_md5hash||
						' , timepoint = '||to_char(log_timepoint,'YYYYMMDD HH24:MI:SS');
					
	RAISE NOTICE '%',activity_string;					
					 
	PERFORM pg_log( log_database_id , 'log_query' , activity_string);  

	EXCEPTION
	  WHEN unique_violation THEN
		RAISE NOTICE '*** unique_violation *** query already has logged';
	END;

	SELECT 	queryid
	INTO   	log_queryid
	FROM 	log_query 
	WHERE 	query_md5hash = log_md5hash AND
			timepoint = log_timepoint;

	IF log_queryid IS NOT NULL 
	THEN 
	  RAISE NOTICE 'log_query with query_md5hash = % and timepoint = % has already has a QUERYID = %',log_md5hash,log_timepoint , log_queryid ;
	  RETURN result;
	END IF;
	
	------------------------------------------------
	RAISE NOTICE 'Update queryid';	
	
	SELECT * 
	INTO log_query_rec
	FROM log_query
	WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ; 
	
	log_query_rec.query=regexp_replace(log_query_rec.query,';+','','g');
	
	FOR pg_stat_history_rec IN
	 SELECT 
         queryid ,
	  query 
	 FROM 
         pg_stat_db_queries 
     WHERE  
      database_id = log_database_id AND
       queryid is not null 
	LOOP
	  pg_stat_query = pg_stat_history_rec.query ; 
	  pg_stat_query=regexp_replace(pg_stat_query,'n+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'t+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,' +',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'$.','%','g');
	
	  log_query_text = trim(trailing ' ' from log_query_rec.query);
	  pg_stat_query_text = pg_stat_query; 
	
	  
	  --SELECT log_query_rec.query like pg_stat_query INTO found_flag ; 
	  IF (log_query_text LIKE pg_stat_query_text) THEN
		found_flag = TRUE ;
	  ELSE
		found_flag = FALSE ;
	  END IF;	  
	  
	  
	  IF found_flag THEN
	    
		UPDATE log_query SET queryid = pg_stat_history_rec.queryid WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ;
		activity_string = 	' updated queryid = '||pg_stat_history_rec.queryid||
		                    ' for log_query with id = '||log_query_rec.id               
		   				    ;						
	    RAISE NOTICE '%',activity_string;	
		EXIT ;
	  END IF ;
	  
	END LOOP ;
	
  RETURN result ;
END
$$ LANGUAGE plpgsql;

ΠŸΡ€ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°Ρ‚Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° сСрвизната маса pg_stat_db_queriesA, ΠΊΠΎΠΉΡ‚ΠΎ ΡΡŠΠ΄ΡŠΡ€ΠΆΠ° ΠΌΠΎΠΌΠ΅Π½Ρ‚Π½Π° снимка Π½Π° Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΡ‚Π΅ заявки ΠΎΡ‚ Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° pg_stat_history (Π˜Π·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° Π΅ описано Ρ‚ΡƒΠΊ βˆ’ ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ Π½Π° производитСлността Π½Π° PostgreSQL заявки. Част 1 - Π΄ΠΎΠΊΠ»Π°Π΄Π²Π°Π½Π΅)

TABLE pg_stat_db_queries
(
   database_id integer,  
   queryid bigint ,  
   query text , 
   max_time double precision 
);

TABLE pg_stat_history 
(
…
database_id integer ,
…
queryid bigint ,
…
max_time double precision	 , 	
…
);

Ѐункцията Π²ΠΈ позволява Π΄Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠΈΡ‚Π΅ Ρ€Π΅Π΄ΠΈΡ†Π° ΠΏΠΎΠ»Π΅Π·Π½ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° заявки ΠΎΡ‚ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ». Π° ΠΈΠΌΠ΅Π½Π½ΠΎ:

Π’ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ β„–1 – Π˜ΡΡ‚ΠΎΡ€ΠΈΡ Π½Π° ΠΈΠ·ΠΏΡŠΠ»Π½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° заявкитС

Много ΠΏΠΎΠ»Π΅Π·Π½ΠΎ Π·Π° стартиранС Π½Π° ΠΈΠ½Ρ†ΠΈΠ΄Π΅Π½Ρ‚ с ΠΈΠ·ΠΏΡŠΠ»Π½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ. ΠŸΡŠΡ€Π²ΠΎ, Π·Π°ΠΏΠΎΠ·Π½Π°ΠΉΡ‚Π΅ сС с историята - ΠΈ ΠΊΠΎΠ³Π° Π·Π°ΠΏΠΎΡ‡Π½Π° забавянСто?
Π‘Π»Π΅Π΄ Ρ‚ΠΎΠ²Π°, спорСд класицитС, Ρ‚ΡŠΡ€ΡΠ΅Ρ‚Π΅ външни ΠΏΡ€ΠΈΡ‡ΠΈΠ½ΠΈ. МоТС Π΄Π° сС ΠΎΠΊΠ°ΠΆΠ΅, Ρ‡Π΅ Π½Π°Ρ‚ΠΎΠ²Π°Ρ€Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ сС Π΅ ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΠ»ΠΎ драстично ΠΈ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π°Ρ‚Π° заявка няма Π½ΠΈΡ‰ΠΎ ΠΎΠ±Ρ‰ΠΎ с Ρ‚ΠΎΠ²Π°.
Π”ΠΎΠ±Π°Π²Π΅Ρ‚Π΅ Π½ΠΎΠ² запис към Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° log_query

  port_start = position('(' in ip_port);
  port_end = position(')' in ip_port);
  client_ip = substring( ip_port from 1 for port_start-1 );
  client_port = substring( ip_port from port_start+1 for port_end-port_start-1 );

  SELECT e.host , d.name , d.owner_pwd 
  INTO database_rec
  FROM database d JOIN endpoint e ON e.id = d.endpoint_id
  WHERE d.id = log_database_id ;
  
  log_timepoint = to_timestamp(log_date||' '||log_time,'YYYY-MM-DD HH24-MI-SS');
  log_duration = to_number(duration,'99999999999999999999D9999999999'); 

  
  pos = position ('SELECT' in UPPER(sql_line) );
  log_query = substring( sql_line from pos for LENGTH(sql_line));
  log_query = regexp_replace(log_query,' +',' ','g');
  log_query = regexp_replace(log_query,';+','','g');
  log_query = trim(trailing ' ' from log_query);
 
  RAISE NOTICE 'log_query=%',log_query ;   

  log_md5hash = md5( log_query::text );
  
  --Explain execution plan--
  EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||database_rec.host||' dbname='||database_rec.name||' user=DATABASE password='||database_rec.owner_pwd||' '')'; 
  
  log_explain_plan = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN '||log_query ) AS t (plan text) );
  log_plan_wo_costs = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN ( COSTS FALSE ) '||log_query ) AS t (plan text) );
    
  PERFORM dblink_disconnect('LINK1');
  --------------------------
  BEGIN
	INSERT INTO log_query
	(
		query_md5hash ,
		database_id , 
		timepoint ,
		duration ,
		query ,
		explained_plan ,
		plan_md5hash , 
		explained_plan_wo_costs , 
		plan_hash_value , 
		ip , 
		port
	) 
	VALUES 
	(
		log_md5hash ,
		log_database_id , 
		log_timepoint , 
		log_duration , 
		log_query ,
		log_explain_plan , 
		md5(log_explain_plan::text) ,
		log_plan_wo_costs , 
		md5(log_plan_wo_costs::text),
		client_ip , 
		client_port		
	);

Ѐункция #2 - Π—Π°ΠΏΠ°Π·Π²Π°Π½Π΅ Π½Π° ΠΏΠ»Π°Π½ΠΎΠ²Π΅ Π·Π° изпълнСниС Π½Π° заявки

Π’ Ρ‚ΠΎΠ·ΠΈ ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΌΠΎΠΆΠ΅ Π΄Π° възникнС Π²ΡŠΠ·Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅-разяснСниС-ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€: β€žΠΠΎ Π²Π΅Ρ‡Π΅ ΠΈΠΌΠ° Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ обяснСниС". Π”Π°, Ρ‚Π°ΠΊΠ° Π΅, Π½ΠΎ какъв Π΅ ΡΠΌΠΈΡΡŠΠ»ΡŠΡ‚, Π°ΠΊΠΎ ΠΏΠ»Π°Π½ΡŠΡ‚ Π·Π° изпълнСниС сС ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π° Π² ΡΡŠΡ‰ΠΈΡ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ» ΠΈ Π·Π° Π΄Π° Π³ΠΎ Π·Π°ΠΏΠ°Π·ΠΈΡ‚Π΅ Π·Π° ΠΏΠΎ-Π½Π°Ρ‚Π°Ρ‚ΡŠΡˆΠ΅Π½ Π°Π½Π°Π»ΠΈΠ·, трябва Π΄Π° Π°Π½Π°Π»ΠΈΠ·ΠΈΡ€Π°Ρ‚Π΅ Π»ΠΎΠ³ Ρ„Π°ΠΉΠ»Π°?

Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‚ΠΎΠ²Π° ΠΈΠΌΠ°Ρ… Π½ΡƒΠΆΠ΄Π° ΠΎΡ‚:
ΠΏΡŠΡ€Π²ΠΎ: ΡΡŠΡ…Ρ€Π°Π½Π΅Ρ‚Π΅ ΠΏΠ»Π°Π½Π° Π·Π° изпълнСниС Π² сСрвизната Ρ‚Π°Π±Π»ΠΈΡ†Π° Π½Π° Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ Π·Π° наблюдСниС;
Π²Ρ‚ΠΎΡ€ΠΎ: Π΄Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° сравняватС ΠΏΠ»Π°Π½ΠΎΠ²Π΅Ρ‚Π΅ Π·Π° изпълнСниС Π΅Π΄ΠΈΠ½ с Π΄Ρ€ΡƒΠ³, Π·Π° Π΄Π° Π²ΠΈΠ΄ΠΈΡ‚Π΅ Π²Π΅Π΄Π½Π°Π³Π°, Ρ‡Π΅ ΠΏΠ»Π°Π½ΡŠΡ‚ Π·Π° изпълнСниС Π½Π° заявката сС Π΅ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΠ».

Налична Π΅ заявка с ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ Π·Π° изпълнСниС. ΠŸΠΎΠ»ΡƒΡ‡Π°Π²Π°Π½Π΅Ρ‚ΠΎ ΠΈ ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π°Π½Π΅Ρ‚ΠΎ Π½Π° нСговия ΠΏΠ»Π°Π½ Π·Π° изпълнСниС с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° EXPLAIN Π΅ Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚Π°Ρ€Π½Π° Π·Π°Π΄Π°Ρ‡Π°.
ОсвСн Ρ‚ΠΎΠ²Π°, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΈΠ·Ρ€Π°Π·Π° EXPLAIN (COSTS FALSE), ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚Π΅ Ρ€Π°ΠΌΠΊΠ°Ρ‚Π° Π½Π° ΠΏΠ»Π°Π½Π°, която Ρ‰Π΅ сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π·Π° ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°Π½Π΅ Π½Π° Ρ…Π΅Ρˆ стойността Π½Π° ΠΏΠ»Π°Π½Π°, ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ‰Π΅ ΠΏΠΎΠΌΠΎΠ³Π½Π΅ ΠΏΡ€ΠΈ послСдващия Π°Π½Π°Π»ΠΈΠ· Π½Π° историята Π½Π° ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚Π΅ Π½Π° ΠΏΠ»Π°Π½Π° Π·Π° изпълнСниС.
Π’Π·Π΅ΠΌΠ΅Ρ‚Π΅ шаблон Π·Π° ΠΏΠ»Π°Π½ Π·Π° изпълнСниС

  --Explain execution plan--
  EXECUTE 'SELECT dblink_connect(''LINK1'',''host='||database_rec.host||' dbname='||database_rec.name||' user=DATABASE password='||database_rec.owner_pwd||' '')'; 
  
  log_explain_plan = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN '||log_query ) AS t (plan text) );
  log_plan_wo_costs = ARRAY ( SELECT * FROM dblink('LINK1', 'EXPLAIN ( COSTS FALSE ) '||log_query ) AS t (plan text) );
    
  PERFORM dblink_disconnect('LINK1');

Π’ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ #3 - ИзползванС Π½Π° Ρ€Π΅Π³ΠΈΡΡ‚ΡŠΡ€Π° Π½Π° заявкитС Π·Π° наблюдСниС

Въй ΠΊΠ°Ρ‚ΠΎ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΠΈΡ‚Π΅ Π·Π° производитСлност са ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Π½ΠΈ Π½Π΅ Π·Π° тСкста Π½Π° заявката, Π° Π·Π° нСйния ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, трябва Π΄Π° ΡΠ²ΡŠΡ€ΠΆΠ΅Ρ‚Π΅ заявки ΠΎΡ‚ рСгистрационния Ρ„Π°ΠΉΠ» със заявки, Π·Π° ΠΊΠΎΠΈΡ‚ΠΎ са ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Π½ΠΈ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚Π΅Π»ΠΈ Π·Π° производитСлност.
Π•, ΠΏΠΎΠ½Π΅ Π·Π° Π΄Π° ΠΈΠΌΠ°ΠΌΠ΅ точния час Π½Π° възникванС Π½Π° ΠΈΠ½Ρ†ΠΈΠ΄Π΅Π½Ρ‚ с изпълнСниС.

По Ρ‚ΠΎΠ·ΠΈ Π½Π°Ρ‡ΠΈΠ½, ΠΊΠΎΠ³Π°Ρ‚ΠΎ възникнС ΠΈΠ½Ρ†ΠΈΠ΄Π΅Π½Ρ‚ с производитСлността Π·Π° ID Π½Π° заявка, Ρ‰Π΅ ΠΈΠΌΠ° ΠΏΡ€Π΅ΠΏΡ€Π°Ρ‚ΠΊΠ° към ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π° заявка със спСцифични стойности Π½Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ ΠΈ Ρ‚ΠΎΡ‡Π½ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π·Π° изпълнСниС ΠΈ ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠΈΡ‚Π΅Π»Π½ΠΎΡΡ‚ Π½Π° заявката. Π’Π·Π΅ΠΌΠ΅Ρ‚Π΅ Π΄Π°Π΄Π΅Π½Π°Ρ‚Π° информация, ΠΊΠ°Ρ‚ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ само ΠΈΠ·Π³Π»Π΅Π΄Π° pg_stat_statements - Π·Π°Π±Ρ€Π°Π½Π΅Π½ΠΎ Π΅.
НамСрСтС queryid Π½Π° заявката ΠΈ Π°ΠΊΡ‚ΡƒΠ°Π»ΠΈΠ·ΠΈΡ€Π°ΠΉΡ‚Π΅ записа Π² Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° log_query

SELECT * 
	INTO log_query_rec
	FROM log_query
	WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ; 
	
	log_query_rec.query=regexp_replace(log_query_rec.query,';+','','g');
	
	FOR pg_stat_history_rec IN
	 SELECT 
      queryid ,
	  query 
	 FROM 
       pg_stat_db_queries 
     WHERE  
	   database_id = log_database_id AND
       queryid is not null 
	LOOP
	  pg_stat_query = pg_stat_history_rec.query ; 
	  pg_stat_query=regexp_replace(pg_stat_query,'n+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'t+',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,' +',' ','g');
	  pg_stat_query=regexp_replace(pg_stat_query,'$.','%','g');
	
	  log_query_text = trim(trailing ' ' from log_query_rec.query);
	  pg_stat_query_text = pg_stat_query; 
	  
	  --SELECT log_query_rec.query like pg_stat_query INTO found_flag ; 
	  IF (log_query_text LIKE pg_stat_query_text) THEN
		found_flag = TRUE ;
	  ELSE
		found_flag = FALSE ;
	  END IF;	  
	  
	  
	  IF found_flag THEN
	    
		UPDATE log_query SET queryid = pg_stat_history_rec.queryid WHERE query_md5hash = log_md5hash AND timepoint = log_timepoint ;
		activity_string = 	' updated queryid = '||pg_stat_history_rec.queryid||
		                    ' for log_query with id = '||log_query_rec.id		                    
		   				    ;						
					
	    RAISE NOTICE '%',activity_string;	
		EXIT ;
	  END IF ;
	  
	END LOOP ;

послСслов

Π’ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ Π½Π° Ρ‚ΠΎΠ²Π° описаният ΠΌΠ΅Ρ‚ΠΎΠ΄ Π½Π°ΠΌΠ΅Ρ€ΠΈ своСто ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π² Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π΅Π½Π° систСма Π·Π° наблюдСниС Π½Π° производитСлността Π½Π° PostgreSQL заявки, ΠΊΠΎΠ΅Ρ‚ΠΎ Π²ΠΈ позволява Π΄Π° ΠΈΠΌΠ°Ρ‚Π΅ ΠΏΠΎΠ²Π΅Ρ‡Π΅ информация Π·Π° Π°Π½Π°Π»ΠΈΠ· ΠΏΡ€ΠΈ Ρ€Π°Π·Ρ€Π΅ΡˆΠ°Π²Π°Π½Π΅ Π½Π° Π²ΡŠΠ·Π½ΠΈΠΊΠ²Π°Ρ‰ΠΈ ΠΈΠ½Ρ†ΠΈΠ΄Π΅Π½Ρ‚ΠΈ с изпълнСниС Π½Π° заявки.

Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‡Π΅, Ρ€Π°Π·Π±ΠΈΡ€Π° сС, ΠΏΠΎ ΠΌΠΎΠ΅ Π»ΠΈΡ‡Π½ΠΎ ΠΌΠ½Π΅Π½ΠΈΠ΅, всС ΠΎΡ‰Π΅ Ρ‰Π΅ Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π° сС Ρ€Π°Π±ΠΎΡ‚ΠΈ Π²ΡŠΡ€Ρ…Ρƒ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΡŠΠΌΠ° Π·Π° ΠΈΠ·Π±ΠΎΡ€ ΠΈ промяна Π½Π° Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π½Π° ΠΈΠ·Ρ‚Π΅Π³Π»Π΅Π½Π°Ρ‚Π° част. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡŠΡ‚ всС ΠΎΡ‰Π΅ Π½Π΅ Π΅ Ρ€Π΅ΡˆΠ΅Π½ Π² общия случай. Π‘ΠΈΠ³ΡƒΡ€Π½ΠΎ Ρ‰Π΅ Π΅ интСрСсно.

Но Ρ‚ΠΎΠ²Π° Π΅ съвсСм Ρ€Π°Π·Π»ΠΈΡ‡Π½Π° история...

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: www.habr.com

ДобавянС Π½Π° Π½ΠΎΠ² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€