Schreiwen eisen eegene capped expirationd Modul fir Tarantool

Schreiwen eisen eegene capped expirationd Modul fir Tarantool

Virun enger Zäit ware mir mam Problem konfrontéiert fir Tupelen a Raum ze botzen tarantool. D'Botzen huet misse gestart ginn, net wann d'Tarantool schonn aus der Erënnerung leeft, awer am Viraus a mat enger gewësser Frequenz. Fir dës Aufgab huet tarantool e Modul geschriwwen an Lua genannt Verfall. Nodeems mir dëse Modul fir eng kuerz Zäit benotzt hunn, hu mir gemierkt datt et fir eis net gëeegent war: Wéinst konstante Botzen vu grousse Quantitéiten un Daten, hänkt Lua am GC. Dofir hu mir geduecht fir eisen eegene capped Expiration Modul z'entwéckelen, an der Hoffnung datt de Code an enger Mammesprooch geschriwwe gëtt eis Probleemer op déi bescht méiglech Manéier léisen.

E gutt Beispill fir eis war den Tarantool Modul genannt memcached. D'Approche, déi an et benotzt gëtt, baséiert op der Tatsaach datt am Raum e separaten Feld erstallt gëtt, wat d'Liewensdauer vum Tupel uginn, an anere Wierder, ttl. De Modul am Hannergrond scannt de Raum, vergläicht den TTL mat der aktueller Zäit an entscheet ob den Tupel geläscht gëtt oder net. De memcached Modulcode ass einfach an elegant, awer ze generesch. Als éischt berücksichtegt et net d'Zort vum Index dee gekrabbelt a geläscht gëtt. Zweetens, op all Pass sinn all Tupels gescannt, d'Zuel vun deenen kann zimlech grouss sinn. A wann am ofgelafte Modul den éischte Problem geléist gouf (de Bamindex gouf an eng separat Klass getrennt), dann huet deen zweeten nach ëmmer keng Opmierksamkeet kritt. Dës dräi Punkten hunn d'Wiel virbestëmmt fir mäin eegene Code ze schreiwen.

Beschreiwung

D'Dokumentatioun fir Tarantool huet eng ganz gutt Tutorial iwwer wéi Dir Är gespäichert Prozeduren am C schreift. Éischtens, ech proposéieren Iech mat Iech vertraut ze maachen fir déi Inserts mat Kommandoen a Code ze verstoen, déi hei drënner erschéngen. Et ass och derwäert opmierksam ze maachen Referenz op Objeten déi disponibel sinn wann Dir Ären eegene capped Modul schreift, nämlech Këscht, Léngen, Index и txn.

Loosst eis vu wäitem ufänken a kucken wéi e capped expirationed Modul vu baussen ausgesäit:

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)

Fir Einfachheet lancéiere mir Tarantool am Verzeechnes wou eis libcapped-expirationd.so Bibliothéik läit. Zwou Funktiounen ginn aus der Bibliothéik exportéiert: Start an Kill. Den éischte Schrëtt ass dës Funktiounen vu Lua verfügbar ze maachen mat box.schema.func.create a box.schema.user.grant. Dann erstellt e Raum deem seng Tupel nëmmen dräi Felder enthält: déi éischt ass en eenzegaartegen Identifizéierer, déi zweet ass E-Mail, an déi drëtt ass d'Liewensdauer vum Tupel. Mir bauen e Bamindex uewen um éischte Feld a nennen et primär. Als nächst kréie mir de Verbindungsobjekt an eis gebierteg Bibliothéik.

No der Virbereedungsaarbecht, lafen d'Startfunktioun aus:

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

Dëst Beispill funktionnéiert beim Scannen genau d'selwecht wéi den ofgelaafte Modul, deen am Lua geschriwwe gëtt. Dat éischt Argument fir d'Startfunktioun ass den eenzegaartegen Numm vun der Aufgab. Déi zweet ass de Raumidentifizéierer. Déi drëtt ass en eenzegaartegen Index, duerch deen d'Tupelen geläscht ginn. Déi véiert ass den Index, duerch deen d'Tupelen duerchgefouert ginn. De fënneften ass d'Zuel vum Tupelfeld mat Liewensdauer (Nummeréierung fänkt vun 1 un, net 0!). De sechsten a siwenten sinn Scannen Astellunge. 1024 ass déi maximal Unzuel vun Tuples déi an enger eenzeger Transaktioun gekuckt kënne ginn. 3600 - Voll Scan Zäit a Sekonnen.

Notéiert datt d'Beispill deeselwechten Index benotzt fir ze krauchen an ze läschen. Wann dëst e Bamindex ass, da gëtt d'Traversal vum méi klenge Schlëssel op dee méi groussen duerchgefouert. Wann et en aneren ass, zum Beispill, Hash Index, da gëtt d'Traversal als Regel an zoufälleg Uerdnung duerchgefouert. All Raum tuples sinn an engem Scan gescannt.

Loosst eis e puer Tupelen an de Raum mat enger Liewensdauer vu 60 Sekonnen setzen:

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}

Loosst eis kucken ob d'Insertioun erfollegräich war:

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

Loosst eis d'Auswiel widderhuelen no 60+ Sekonnen (zählen vum Ufank vun der Insertioun vum éischten Tupel) a kuckt datt de capped expirationed Modul scho veraarbecht ass:

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

Loosst eis d'Aufgab ophalen:

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

Loosst eis en zweet Beispill kucken wou e separaten Index fir de Crawl benotzt gëtt:

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)

Alles hei ass d'selwecht wéi am éischte Beispill, mat e puer Ausnahmen. Mir bauen e Bamindex uewen um drëtte Feld a nennen et exp. Dësen Index muss net eenzegaarteg sinn, am Géigesaz zum Index genannt Primär. Traversal gëtt duerch exp Index duerchgefouert, a Läschung duerch Primär. Mir erënneren datt fréier béid nëmme mam primären Index gemaach goufen.

No der Virbereedungsaarbecht lafen mir d'Startfunktioun mat neien Argumenter:

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

Loosst eis mat enger Liewensdauer vu 60 Sekonnen erëm e puer Tupelen an de Raum setzen:

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}

No 30 Sekonnen, no Analogie, addéiere mer e puer méi Tupels:

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}

Loosst eis kucken ob d'Insertioun erfollegräich war:

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]
...

Loosst eis d'Auswiel widderhuelen no 60+ Sekonnen (zählen vum Ufank vun der Insertioun vum éischten Tupel) a kuckt datt de capped expirationed Modul scho veraarbecht ass:

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

Et sinn nach e puer Tupelen am Raum, déi ongeféier 30 Sekonnen méi ze liewen hunn. Ausserdeem huet de Scan gestoppt wann Dir vun engem Tupel mat enger ID vun 2 an enger Liewensdauer vun 1576421257 op en Tupel mat enger ID vun 3 an enger Liewensdauer vun 1576421287 geplënnert ass. d'exp Index Schlësselen. Dëst sinn d'Erspuernisser, déi mir am Ufank wollten erreechen.

Loosst eis d'Aufgab ophalen:

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

Ëmsetzung

De beschte Wee fir iwwer all d'Features vun engem Projet ze erzielen ass seng originell Quell. code! Als Deel vun der Verëffentlechung wäerte mir nëmmen op déi wichtegst Punkte konzentréieren, nämlech Space Contournement Algorithmen.

D'Argumenter, déi mir un d'Startmethod passéieren, ginn an enger Struktur gespäichert genannt 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;
};

Den Numm Attribut ass den Numm vun der Aufgab. D'Space_id Attribut ass de Raumidentifizéierer. Den Attribut rm_index_id ass den Identifizéierer vum eenzegaartegen Index, duerch deen d'Tupelen geläscht ginn. D'it_index_id Attribut ass den Identifizéierer vum Index, duerch deen d'Tupelen duerchgefouert ginn. D'it_index_type Attribut ass den Typ vum Index, duerch deen d'Tupelen duerchgestrachenem ginn. De filed_no Attribut ass d'Zuel vum Tupelfeld mat Liewensdauer. D'Scan_size Attribut ass déi maximal Unzuel vun Tuples déi an enger Transaktioun gescannt ginn. De scan_time Attribut ass déi voll Scanzäit a Sekonnen.

Mir wäerten d'Argumenter net analyséieren. Dëst ass eng ustrengend awer einfach Aarbecht, mat där d'Bibliothéik Iech hëlleft msgpuck. Schwieregkeete kënnen nëmme mat Indizes entstoen, déi vu Lua als komplexer Datestruktur mam mp_map-Typ passéiert sinn, an net déi einfach Typen mp_bool, mp_double, mp_int, mp_uint an mp_array benotzen. Awer et ass net néideg de ganzen Index ze analyséieren. Dir musst just seng Eenzegaartegkeet kontrolléieren, den Typ berechnen an den Identifizéierer extrahéieren.

Mir lëschten d'Prototypen vun alle Funktiounen déi fir d'Parsing benotzt ginn:

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);

Loosst eis elo op déi wichtegst Saach réckelen - d'Logik vum Raum ëmzegoen an Tuples ze läschen. All Block vun Tuples net méi grouss wéi scan_size gëtt gescannt a geännert ënner enger eenzeger Transaktioun. Wann et erfollegräich ass, ass dës Transaktioun engagéiert; wann de Feeler geschitt, gëtt se zréckgezunn. Dat lescht Argument fir d'Expirationd_iterate Funktioun ass e Pointer op den Iterator, aus deem d'Scannen ufänkt oder weider geet. Dësen Iterator gëtt intern eropgebaut bis e Feeler geschitt, de Raum leeft oder et ass net méiglech de Prozess am Viraus ze stoppen. D'Funktioun expirationd_expired kontrolléiert d'Liewensdauer vun engem Tupel, expirationd_delete läscht en Tupel, expirationd_breakable kontrolléiert ob mir weider musse goen.

Expirationd_iterate Funktiounscode:

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

Funktiounscode 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 Funktiounscode:

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

Funktiounscode expiration_breakable:

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

Applikatioun

Dir kënnt de Quellcode kucken op hei!

Source: will.com

Setzt e Commentaire