ПишСм свой capped expirationd ΠΌΠΎΠ΄ΡƒΠ»ΡŒ для tarantool

ПишСм свой capped expirationd ΠΌΠΎΠ΄ΡƒΠ»ΡŒ для tarantool

КакоС-Ρ‚ΠΎ врСмя Π½Π°Π·Π°Π΄ ΠΏΠ΅Ρ€Π΅Π΄ Π½Π°ΠΌΠΈ встала ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° чистки ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ Π² спСйсах tarantool. Чистку Π½ΡƒΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ Π½Π΅ Ρ‚ΠΎΠ³Π΄Π°, ΠΊΠΎΠ³Π΄Π° Ρƒ tarantool ΡƒΠΆΠ΅ Π·Π°ΠΊΠ°Π½Ρ‡ΠΈΠ²Π°Π»Π°ΡΡŒ ΠΏΠ°ΠΌΡΡ‚ΡŒ, Π° Π·Π°Ρ€Π°Π½Π΅Π΅ ΠΈ с ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠΉ ΠΏΠ΅Ρ€ΠΈΠΎΠ΄ΠΈΡ‡Π½ΠΎΡΡ‚ΡŒΡŽ. Для этой Π·Π°Π΄Π°Ρ‡ΠΈ Π² tarantool Π΅ΡΡ‚ΡŒ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ, написанный Π½Π° Lua, ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ expirationd. ПослС Π½Π΅ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ использования этого модуля ΠΌΡ‹ поняли, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ ΠΎΠ½ Π½Π΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚: Π½Π° постоянных чистках Π±ΠΎΠ»ΡŒΡˆΠΈΡ… объСмов Π΄Π°Π½Π½Ρ‹Ρ… Lua висСл Π² GC. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΠΌΡ‹ Π·Π°Π΄ΡƒΠΌΠ°Π»ΠΈΡΡŒ ΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ своСго capped expirationd модуля, надСясь, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ΄, написанный Π½Π° Π½Π°Ρ‚ΠΈΠ²Π½ΠΎΠΌ языкС программирования, Ρ€Π΅ΡˆΠΈΡ‚ наши Π·Π°Π΄Π°Ρ‡ΠΈ Π½Π°ΠΈΠ»ΡƒΡ‡ΡˆΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ.

Π₯ΠΎΡ€ΠΎΡˆΠΈΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠΌ Π½Π°ΠΌ послуТил ΠΌΠΎΠ΄ΡƒΠ»ΡŒ tarantool ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ memcached. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹ΠΉ Π² Π½Π΅ΠΌ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ основываСтся Π½Π° Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π² спСйсС заводится ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠ΅ ΠΏΠΎΠ»Π΅, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ указываСтся врСмя ΠΆΠΈΠ·Π½ΠΈ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ°, ΠΈΠ½Ρ‹ΠΌΠΈ словами, ttl. ΠœΠΎΠ΄ΡƒΠ»ΡŒ Π² Ρ„ΠΎΠ½Π΅ сканируСт спСйс, сравниваСт ttl с Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΈ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΎ Ρ‚ΠΎΠΌ, ΡƒΠ΄Π°Π»ΡΡ‚ΡŒ ΠΊΠΎΡ€Ρ‚Π΅ΠΆ ΠΈΠ»ΠΈ Π½Π΅Ρ‚. Код модуля memcached простой ΠΈ элСгантный, Π½ΠΎ слишком ΠΎΠ±Ρ‰ΠΈΠΉ. Π’ΠΎ-ΠΏΠ΅Ρ€Π²Ρ‹Ρ…, ΠΎΠ½ Π½Π΅ ΡƒΡ‡ΠΈΡ‚Ρ‹Π²Π°Π΅Ρ‚ Ρ‚ΠΈΠΏ индСкса, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ осущСствляСтся ΠΎΠ±Ρ…ΠΎΠ΄ ΠΈ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅. Π’ΠΎ-Π²Ρ‚ΠΎΡ€Ρ‹Ρ…, Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π΅ ΡΠΊΠ°Π½ΠΈΡ€ΡƒΡŽΡ‚ΡΡ всС ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠΈ, количСство ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ довольно большим. И Ссли Π² ΠΌΠΎΠ΄ΡƒΠ»Π΅ expirationd пСрвая ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π±Ρ‹Π»Π° Ρ€Π΅ΡˆΠ΅Π½Π° (tree-индСкс Π²Ρ‹Π΄Π΅Π»Π΅Π½ Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ класс), Ρ‚ΠΎ Π²Ρ‚ΠΎΡ€ΠΎΠΉ всС Ρ‚Π°ΠΊ ΠΆΠ΅ Π½Π΅ ΡƒΠ΄Π΅Π»Π΅Π½ΠΎ Π½ΠΈΠΊΠ°ΠΊΠΎΠ³ΠΎ внимания. Π­Ρ‚ΠΈ Ρ‚Ρ€ΠΈ ΠΏΡƒΠ½ΠΊΡ‚Π° ΠΏΡ€Π΅Π΄ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠ»ΠΈ Π²Ρ‹Π±ΠΎΡ€ Π² ΠΏΠΎΠ»ΡŒΠ·Ρƒ написания своСго ΠΊΠΎΠ΄Π°.

ОписаниС

Π’ Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ ΠΊ tarantool Π΅ΡΡ‚ΡŒ ΠΎΡ‡Π΅Π½ΡŒ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΉ Ρ‚ΡƒΡ‚ΠΎΡ€ΠΈΠ°Π» ΠΎ Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊ ΠΏΠΈΡΠ°Ρ‚ΡŒ свои Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Π΅ ΠΏΡ€ΠΎΡ†Π΅Π΄ΡƒΡ€Ρ‹ Π½Π° языкС C. Π’ ΠΏΠ΅Ρ€Π²ΡƒΡŽ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΏΡ€Π΅Π΄Π»Π°Π³Π°ΡŽ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с Π½ΠΈΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ½ΠΈΠΌΠ°Ρ‚ΡŒ Ρ‚Π΅ вставки с ΠΊΠΎΠΌΠ°Π½Π΄Π°ΠΌΠΈ ΠΈ ΠΊΠΎΠ΄ΠΎΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄ΡƒΡ‚ Π²ΡΡ‚Ρ€Π΅Ρ‡Π°Ρ‚ΡŒΡΡ Π½ΠΈΠΆΠ΅. Π’Π°ΠΊΠΆΠ΅ стоит ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° рСфСрСнс ΠΊ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ доступны ΠΏΡ€ΠΈ написании своСго собствСнного capped модуля, Π° ΠΈΠΌΠ΅Π½Π½ΠΎ Π½Π° box, fiber, index ΠΈ txn.

Π”Π°Π²Π°ΠΉΡ‚Π΅ Π½Π°Ρ‡Π½Π΅ΠΌ ΠΈΠ·Π΄Π°Π»Π΅ΠΊΠ° ΠΈ посмотрим Π½Π° Ρ‚ΠΎ, ΠΊΠ°ΠΊ выглядит capped expirationd ΠΌΠΎΠ΄ΡƒΠ»ΡŒ снаруТи:

fiber = require('fiber')
net_box = require('net.box')
box.cfg{listen = 3300}
box.schema.func.create('libcapped-expirationd.start', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.start')
box.schema.func.create('libcapped-expirationd.kill', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.kill')
box.schema.space.create('tester')
box.space.tester:create_index('primary', {unique = true, parts = {1, 'unsigned'}})
capped_connection = net_box:new(3300)

Для простоты запускаСм tarantool Π² ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π΅, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ находится наша Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° libcapped-expirationd.so. Из Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΡΠΊΡΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π΄Π²Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ: start ΠΈ kill. Π’Π½Π°Ρ‡Π°Π»Π΅ самом Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ эти Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ доступными ΠΈΠ· Lua с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ box.schema.func.create ΠΈ box.schema.user.grant. Π—Π°Ρ‚Π΅ΠΌ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ спСйс, ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠΈ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π±ΡƒΠ΄ΡƒΡ‚ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ всСго Ρ‚Ρ€ΠΈ поля: ΠΏΠ΅Ρ€Π²ΠΎΠ΅ β€” это ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, Π²Ρ‚ΠΎΡ€ΠΎΠ΅ β€” элСктронная ΠΏΠΎΡ‡Ρ‚Π°, Ρ‚Ρ€Π΅Ρ‚ΡŒΠ΅ β€” врСмя ΠΆΠΈΠ·Π½ΠΈ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ°. ΠŸΠΎΠ²Π΅Ρ€Ρ… ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ поля строим tree-индСкс ΠΈ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌ Π΅Π³ΠΎ primary. Π”Π°Π»Π΅Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΊ нашСй Π½Π°Ρ‚ΠΈΠ²Π½ΠΎΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ΅.

ПослС ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ€Π°Π±ΠΎΡ‚ запускаСм Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ start:

capped_connection:call('libcapped-expirationd.start', {'non-indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.primary, 3, 1024, 3600})

Π­Ρ‚ΠΎΡ‚ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΏΡ€ΠΈ сканировании Ρ€ΠΎΠ²Π½ΠΎ Ρ‚Π°ΠΊΠΆΠ΅, ΠΊΠ°ΠΊ ΠΈ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ expirationd, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ написан Π½Π° Lua. ΠŸΠ΅Ρ€Π²Ρ‹ΠΌ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ Π² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ start пСрСдаСтся ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠ΅ имя таска. Π’Ρ‚ΠΎΡ€Ρ‹ΠΌ β€” ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ спСйса. Π’Ρ€Π΅Ρ‚ΡŒΠΈΠΌ β€” ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ индСкс, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ. Π§Π΅Ρ‚Π²Π΅Ρ€Ρ‚Ρ‹ΠΌ β€” индСкс, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ ΠΎΠ±Ρ…ΠΎΠ΄ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ. ΠŸΡΡ‚Ρ‹ΠΌ β€” Π½ΠΎΠΌΠ΅Ρ€ поля ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ° с Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ (нумСрация ΠΈΠ΄Π΅Ρ‚ ΠΎΡ‚ 1, Π° Π½Π΅ 0!). ШСстым ΠΈ ΡΠ΅Π΄ΡŒΠΌΡ‹ΠΌ β€” настройки сканирования. 1024 β€” это максимальноС количСство ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ просматриваСтся Π² Ρ€Π°ΠΌΠΊΠ°Ρ… ΠΎΠ΄Π½ΠΎΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ. 3600 β€” врСмя ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ сканирования Π² сСкундах.

ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ для ΠΎΠ±Ρ…ΠΎΠ΄Π° ΠΈ удалСния Π² ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ΄ΠΈΠ½ ΠΈ Ρ‚ΠΎΡ‚ ΠΆΠ΅ индСкс. Если это tree-индСкс, Ρ‚ΠΎ ΠΎΠ±Ρ…ΠΎΠ΄ осущСствляСтся ΠΎΡ‚ мСньшСго ΠΊΠ»ΡŽΡ‡Π° ΠΊ Π±ΠΎΠ»ΡŒΡˆΠ΅ΠΌΡƒ. Если ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ Π΄Ρ€ΡƒΠ³ΠΎΠΉ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, hash-индСкс, Ρ‚ΠΎ ΠΎΠ±Ρ…ΠΎΠ΄ осущСствляСтся, ΠΊΠ°ΠΊ ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ, Π² ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½ΠΎΠΌ порядкС. Π—Π° ΠΎΠ΄Π½ΠΎ сканированиС ΠΏΡ€ΠΎΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°ΡŽΡ‚ΡΡ всС ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠΈ спСйса.

Π”Π°Π²Π°ΠΉΡ‚Π΅ сдСлаСм вставку Π² спСйс Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ с Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ 60 сСкунд:

box.space.tester:insert{0, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, '[email protected]', math.floor(fiber.time()) + 60}

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ, Ρ‡Ρ‚ΠΎ вставка ΠΏΡ€ΠΎΡˆΠ»Π° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ:

tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576418976]
  - [1, '[email protected]', 1576418976]
  - [2, '[email protected]', 1576418976]
...

ΠŸΠΎΠ²Ρ‚ΠΎΡ€ΠΈΠΌ select Ρ‡Π΅Ρ€Π΅Π· 60+ сСкунд (считаСм ΠΎΡ‚ Π½Π°Ρ‡Π°Π»Π° вставки ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ°) ΠΈ ΡƒΠ²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ capped expirationd ΡƒΠΆΠ΅ ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚Π°Π»:

tarantool> box.space.tester.index.primary:select()
---
  - []
...

ΠžΡΡ‚Π°Π½ΠΎΠ²ΠΈΠΌ таск:

capped_connection:call('libcapped-expirationd.kill', {'non-indexed'})

Π”Π°Π²Π°ΠΉΡ‚Π΅ рассмотрим Π²Ρ‚ΠΎΡ€ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΊΠΎΠ³Π΄Π° для ΠΎΠ±Ρ…ΠΎΠ΄Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ индСкс:

fiber = require('fiber')
net_box = require('net.box')
box.cfg{listen = 3300}
box.schema.func.create('libcapped-expirationd.start', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.start')
box.schema.func.create('libcapped-expirationd.kill', {language = 'C'})
box.schema.user.grant('guest', 'execute', 'function', 'libcapped-expirationd.kill')
box.schema.space.create('tester')
box.space.tester:create_index('primary', {unique = true, parts = {1, 'unsigned'}})
box.space.tester:create_index('exp', {unique = false, parts = {3, 'unsigned'}})
capped_connection = net_box:new(3300)

Π—Π΄Π΅ΡΡŒ всС Ρ‚ΠΎ ΠΆΠ΅ самоС, Ρ‡Ρ‚ΠΎ ΠΈ Π² ΠΏΠ΅Ρ€Π²ΠΎΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅, Π·Π° ΠΌΠ°Π»Ρ‹ΠΌ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ΠΌ. ΠŸΠΎΠ²Π΅Ρ€Ρ… Ρ‚Ρ€Π΅Ρ‚ΡŒΠ΅Π³ΠΎ поля строим tree-индСкс ΠΈ Π½Π°Π·Ρ‹Π²Π°Π΅ΠΌ Π΅Π³ΠΎ exp. Π­Ρ‚ΠΎΡ‚ индСкс Π½Π΅ обязан Π±Ρ‹Ρ‚ΡŒ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ, Π² ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΡ‚ индСкса ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ primary. ΠžΠ±Ρ…ΠΎΠ΄ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ ΠΏΠΎ exp индСксу, Π° ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎ primary. ΠœΡ‹ ΠΏΠΎΠΌΠ½ΠΈΠΌ, Ρ‡Ρ‚ΠΎ Ρ€Π°Π½Π΅Π΅ ΠΈ Ρ‚ΠΎ, ΠΈ Π΄Ρ€ΡƒΠ³ΠΎΠ΅ дСлалось Ρ‚ΠΎΠ»ΡŒΠΊΠΎ с использованиСм primary индСкса.

ПослС ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… Ρ€Π°Π±ΠΎΡ‚ запускаСм Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ start с Π½ΠΎΠ²Ρ‹ΠΌΠΈ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°ΠΌΠΈ:

capped_connection:call('libcapped-expirationd.start', {'indexed', box.space.tester.id, box.space.tester.index.primary, box.space.tester.index.exp, 3, 1024, 3600})

Π‘Π½ΠΎΠ²Π° сдСлаСм вставку Π² спСйс Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ с Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ 60 сСкунд:

box.space.tester:insert{0, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{1, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{2, '[email protected]', math.floor(fiber.time()) + 60}

Π§Π΅Ρ€Π΅Π· 30 сСкунд ΠΏΠΎ Π°Π½Π°Π»ΠΎΠ³ΠΈΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π΅Ρ‰Π΅ нСсколько ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ:

box.space.tester:insert{3, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{4, '[email protected]', math.floor(fiber.time()) + 60}
box.space.tester:insert{5, '[email protected]', math.floor(fiber.time()) + 60}

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ, Ρ‡Ρ‚ΠΎ вставка ΠΏΡ€ΠΎΡˆΠ»Π° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ:

tarantool> box.space.tester.index.primary:select()
---
- - [0, '[email protected]', 1576421257]
  - [1, '[email protected]', 1576421257]
  - [2, '[email protected]', 1576421257]
  - [3, '[email protected]', 1576421287]
  - [4, '[email protected]', 1576421287]
  - [5, '[email protected]', 1576421287]
...

ΠŸΠΎΠ²Ρ‚ΠΎΡ€ΠΈΠΌ select Ρ‡Π΅Ρ€Π΅Π· 60+ сСкунд (считаСм ΠΎΡ‚ Π½Π°Ρ‡Π°Π»Π° вставки ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ°) ΠΈ ΡƒΠ²ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ capped expirationd ΡƒΠΆΠ΅ ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚Π°Π»:

tarantool> box.space.tester.index.primary:select()
---
- - [3, '[email protected]', 1576421287]
  - [4, '[email protected]', 1576421287]
  - [5, '[email protected]', 1576421287]
...

Π’ спСйсС ΠΎΡΡ‚Π°Π»ΠΈΡΡŒ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ ΠΆΠΈΡ‚ΡŒ Π΅Ρ‰Π΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ 30 сСкунд. Π‘ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ³ΠΎ, сканированиС ΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΈΠ»ΠΎΡΡŒ ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π΅ ΠΎΡ‚ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ° с ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ 2 ΠΈ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ 1576421257 ΠΊ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΡƒ с ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠΌ 3 ΠΈ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ 1576421287. ΠšΠΎΡ€Ρ‚Π΅ΠΆΠΈ с Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ 1576421287 ΠΈ Π±ΠΎΠ»Π΅Π΅ просмотрСны Π½Π΅ Π±Ρ‹Π»ΠΈ Π·Π° счСт упорядочСнности ΠΊΠ»ΡŽΡ‡Π΅ΠΉ exp индСкса. Π­Ρ‚Π° ΠΈ Π΅ΡΡ‚ΡŒ Ρ‚Π° экономия, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΌΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ Π΄ΠΎΠ±ΠΈΡ‚ΡŒΡΡ Π² самом Π½Π°Ρ‡Π°Π»Π΅.

ΠžΡΡ‚Π°Π½ΠΎΠ²ΠΈΠΌ таск:

capped_connection:call('libcapped-expirationd.kill', {'indexed'})

РСализация

Π›ΡƒΡ‡ΡˆΠ΅ всСго ΠΎ всСх особСнностях ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° всСгда расскаТСт Π΅Π³ΠΎ исходный ΠΊΠΎΠ΄! Π’ Ρ€Π°ΠΌΠΊΠ°Ρ… ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΌΡ‹ остановимся лишь Π½Π° самых Π²Π°ΠΆΠ½Ρ‹Ρ… ΠΌΠΎΠΌΠ΅Π½Ρ‚Π°Ρ…, Π° ΠΈΠΌΠ΅Π½Π½ΠΎ, Π½Π° Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ°Ρ… ΠΎΠ±Ρ…ΠΎΠ΄Π° спСйса.

АргумСнты, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅ΠΌ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄ start, ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ Π² структурС ΠΏΠΎΠ΄ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ΠΌ expirationd_task:

struct expirationd_task
{
  char name[256];
  uint32_t space_id;
  uint32_t rm_index_id;
  uint32_t it_index_id;
  uint32_t it_index_type; 
  uint32_t field_no;
  uint32_t scan_size;
  uint32_t scan_time;
};

Атрибут name β€” это имя таска. Атрибут space_id β€” ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ спСйса. Атрибут rm_index_id β€” ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ индСкса, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ. Атрибут it_index_id β€” ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ индСкса, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ ΠΎΠ±Ρ…ΠΎΠ΄ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ. Атрибут it_index_type β€” Ρ‚ΠΈΠΏ индСкса, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ ΠΎΠ±Ρ…ΠΎΠ΄ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ. Атрибут filed_no β€” Π½ΠΎΠΌΠ΅Ρ€ поля ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ° с Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ ΠΆΠΈΠ·Π½ΠΈ. Атрибут scan_size β€” максимальноС количСство ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ просматриваСтся Π² Ρ€Π°ΠΌΠΊΠ°Ρ… ΠΎΠ΄Π½ΠΎΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ. Атрибут scan_time β€” врСмя ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ сканирования Π² сСкундах.

ΠŸΠ°Ρ€ΡΠΈΠ½Π³ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠ² Ρ€Π°ΡΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ. Π­Ρ‚ΠΎ кропотливая, Π½ΠΎ нСслоТная Ρ€Π°Π±ΠΎΡ‚Π°, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π²Π°ΠΌ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° msgpuck. Врудности ΠΌΠΎΠ³ΡƒΡ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ лишь с индСксами, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ ΠΈΠ· Lua Π² Π²ΠΈΠ΄Π΅ слоТной структуры Π΄Π°Π½Π½Ρ‹Ρ… с Ρ‚ΠΈΠΏΠΎΠΌ mp_map, Π° Π½Π΅ с ΠΏΠΎΠΌΠΎΡ‰ΡŒ простых Ρ‚ΠΈΠΏΠΎΠ² mp_bool, mp_double, mp_int, mp_uint ΠΈ mp_array. Но вСсь индСкс ΠΏΠ°Ρ€ΡΠΈΡ‚ΡŒ ΠΈ Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ. Достаточно лишь ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π΅Π³ΠΎ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ, Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ Ρ‚ΠΈΠΏ ΠΈ ΠΈΠ·Π²Π»Π΅Ρ‡ΡŒ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€.

ΠŸΠ΅Ρ€Π΅Ρ‡ΠΈΡΠ»ΠΈΠΌ ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏΡ‹ всСх Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ для парсинга:

bool expirationd_parse_name(struct expirationd_task *task, const char **pos);
bool expirationd_parse_space_id(struct expirationd_task *task, const char **pos);
bool expirationd_parse_rm_index_id(struct expirationd_task *task, const char **pos);
bool expirationd_parse_rm_index_unique(struct expirationd_task *task, const char **pos);
bool expirationd_parse_rm_index(struct expirationd_task *task, const char **pos);
bool expirationd_parse_it_index_id(struct expirationd_task *task, const char **pos);
bool expirationd_parse_it_index_type(struct expirationd_task *task, const char **pos);
bool expirationd_parse_it_index(struct expirationd_task *task, const char **pos);
bool expirationd_parse_field_no(struct expirationd_task *task, const char **pos);
bool expirationd_parse_scan_size(struct expirationd_task *task, const char **pos);
bool expirationd_parse_scan_time(struct expirationd_task *task, const char **pos);

А Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΠ΅Ρ€Π΅ΠΉΠ΄Π΅ΠΌ ΠΊ самому Π³Π»Π°Π²Π½ΠΎΠΌΡƒ β€” ΠΊ Π»ΠΎΠ³ΠΈΠΊΠ΅ ΠΎΠ±Ρ…ΠΎΠ΄Π° спСйса ΠΈ удалСния ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ. ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ Π±Π»ΠΎΠΊ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ΅ΠΉ Ρ€Π°Π·ΠΌΠ΅Ρ€ΠΎΠΌ Π½Π΅ Π±ΠΎΠ»Π΅Π΅ scan_size просматриваСтся ΠΈ измСняСтся ΠΏΠΎΠ΄ ΠΎΠ΄Π½ΠΎΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠ΅ΠΉ. Π’ случаС успСха эта транзакция коммитится, Π² случаС ошибки β€” откатываСтся. ПослСдним Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ Π² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ expirationd_iterate пСрСдаСтся ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€, с ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ начинаСтся ΠΈΠ»ΠΈ продолТаСтся сканированиС. Π­Ρ‚ΠΎΡ‚ ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΎΡ€ инкрСмСнтируСтся Π²Π½ΡƒΡ‚Ρ€ΠΈ ΠΏΠΎΠΊΠ° Π½Π΅ ΠΏΡ€ΠΎΠΈΠ·ΠΎΠΉΠ΄Π΅Ρ‚ ошибка, Π½Π΅ закончится спСйс, Π»ΠΈΠ±ΠΎ Π½Π΅ прСдставится возмоТности Π·Π°Ρ€Π°Π½Π΅Π΅ ΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ процСсс. Ѐункция expirationd_expired провСряСт врСмя ΠΆΠΈΠ·Π½ΠΈ ΠΊΠΎΡ€Ρ‚Π΅ΠΆΠ°, expirationd_delete β€” удаляСт ΠΊΠΎΡ€Ρ‚Π΅ΠΆ, expirationd_breakable β€” провСряСт, Π½ΡƒΠΆΠ½ΠΎ Π»ΠΈ Π½Π°ΠΌ Π΄Π²ΠΈΠ³Π°Ρ‚ΡŒΡΡ дальшС.

Код Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ expirationd_iterate:

static bool
expirationd_iterate(struct expirationd_task *task, box_iterator_t **iterp)
{
  box_iterator_t *iter = *iterp;
  box_txn_begin();
  for (uint32_t i = 0; i < task->scan_size; ++i) {
    box_tuple_t *tuple = NULL;
    if (box_iterator_next(iter, &tuple) < 0) {
      box_iterator_free(iter);
      *iterp = NULL;
      box_txn_rollback();
      return false;
    }
    if (!tuple) {
      box_iterator_free(iter);
      *iterp = NULL;
      box_txn_commit();
      return true;
    }
    if (expirationd_expired(task, tuple))
      expirationd_delete(task, tuple);
    else if (expirationd_breakable(task))
      break;
  }
  box_txn_commit();
  return true;
}

Код Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ expirationd_expired:

static bool
expirationd_expired(struct expirationd_task *task, box_tuple_t *tuple)
{
  const char *buf = box_tuple_field(tuple, task->field_no - 1);
  if (!buf || mp_typeof(*buf) != MP_UINT)
    return false;
  uint64_t val = mp_decode_uint(&buf);
  if (val > fiber_time64() / 1000000)
    return false;
  return true;
}

Код Ρ„ΡƒΠΊΠ½Ρ†ΠΈΠΈ expirationd_delete:

static void
expirationd_delete(struct expirationd_task *task, box_tuple_t *tuple)
{
  uint32_t len;
  const char *str = box_tuple_extract_key(tuple, task->space_id, task->rm_index_id, &len);
  box_delete(task->space_id, task->rm_index_id, str, str + len, NULL);
}

Код Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ expirationd_breakable:

static bool
expirationd_breakable(struct expirationd_task *task)
{
  return task->it_index_id != task->rm_index_id && task->it_index_type == ITER_GT;
}

ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅

ΠžΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с исходным ΠΊΠΎΠ΄ΠΎΠΌ ΠΌΠΎΠΆΠ½ΠΎ Π½Π° Ρ‚ΡƒΡ‚!

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