რჩევები საიტის ოპტიმიზაციისთვის (Front end)

დაახლოებით ერთი თვის წინ GeOlymp-ის საიტის გარეგნული მხარე განვაახლეთ თუმცა ბრაუზერის მხარეს სწრაფად ჩატვირთვისთვის საჭირო ოპტიმიზაცია არ გამიკეთებია. ამ პოსტში შევეცდები იმ თემებზე ვისაუბრო, რასაც ამ პროცესზე აქვს გავლენა; ამასთან, რეალურად შევასრულო ისინი საიტზე და ჩატვირთვის დროები შევადარო.

ახლა ქრომი მაჩვენებს, რომ ჯეოლიმპის ერთ-ერთი მთავარი გვერდის გახსნის დროს სერვერზე 42 მოთხოვნა იგზავნება, 307.9 კილობაიტი იტვირთება და 1.2 – 2.3 წამს ანდომებს გვერდის ჩატვირთვას. შევეცდები ამის გაუმჯობესებას..

1. ნაკლები რაოდენობის HTTP მოთხოვნის გაგზავნა სერვერზე
ერთ-ერთი ყველაზე მნიშვნელოვანი წესი, რომელსაც შესამჩნევი შედეგი აქვს, სერვერზე გასაგზავნი HTTP მოთხოვნების შემცირებაა. ეს მოთხოვნები იგზავნება თვითონ html ფაილის და ასევე თითოეული კომპონენტის წამოსაღებად, რასაც საიტი შეიცავს (სურათები, css და javascript ფაილები, ა.შ.).

http მოთხოვნა საკმაოდ მძიმე ოპერაციაა, რადგან მის შესასრულებლად საჭიროა DNS სერვერთან მისვლა, დომენის შესაბამისი ip მისამართის მიღება, მიღებული მისამართის საშუალებით ჰოსტ სერვერის მიგნება და ბოლო ბოლო ვებსერვერიდან რესურსის წამოღება. თუ ეს პუნქტები გეოგრაფიულადაც დაშორებულია (მაგალითად სხვადასხვა ქვეყნებში), მოთხოვნას უფრო მეტი სერვერის და ქსელის გავლა უწევს და დრო შესაბამისად იზრდება (ილუსტრაცია: როგორ მუშაობს ინტერნეტი).

მართალია, ბრაუზერს შეუძლია დომენიც და რესურსებსაც კეშში შეინახოს, მაგრამ როდესაც მომხმარებელი პირველად ხსნის გვერდს ან კეში ცარიელი აქვს, მისთვის საიტის ჩატვირთვა ბევრად დიდ დროს წაიღებს. მე პირადად არ შემიდგენია სტატისტიკა, თუმცა, მაგალითად, Yahoo-მ ჩაატარა გამოკვლევა, რომ ენახა რამდენი მომხმარებელი ნახულობდა საიტს ცარიელი კეშით და იმ დროს მათი რაოდენობა საშუალოდ მომხმარებლების 40-60% პროცენტი გამოვიდა.

საიტის სხვადასხვა კომპონენტების ჩამოტვირთვას კიდევ ერთი ცუდი თვისება აქვს. ისინი პარალელურ რეჟიმში არ იტვირთებიან, არამედ ერთმანეთს ელოდებიან.

HTTP / 1.1 სპეციფიკაციის მიხედვით რეკომენდირებულია, რომ კლიენტიდან ერთ დომენთან პარალელურად მხოლოდ ერთი ან ორი კავშირი არსებობდეს. თუმცა ახალი ბრაუზერები ამ რეკომენდაციას არ ითვალისწინებდნენ და 4-მდე ან 6-მდე აქვთ რაოდენობა შეზღუდული. ეს პარამეტრი მომხმარებელს შეუძლია თავისი ბრაუზერის კონფიგურაციიდან შეცვალოს და კავშირების ლიმიტი გაზარდოს. შესაბამისად, მეტი რესურსი ჩამოტვირთოს პარალელურად.

ერთდროულად ჩატვირთების რაოდენობა მომხმარებლისგან დამოუკიდებლადაც შეიძლება გაიზარდოს, რადგან შეზღუდვა დომენის სახელს უკავშირდება. თუ ჩვენ რესურსებს სხვადასხვა სერვერზე ან დომენების ალიასებზე და ქვედომენებზე გავანაწილებთ, მაშინ ბრაუზერები მათ პარალელურად წაიღებენ. აქ უნდა აღინიშნოს, რომ ასეთი ჩატვირთვების რაოდენობის გაზრდის პროპორციულად კლიენტის პროცესორზე იზრდება დატვირთვა. რა თქმა უნდა, ეს ძალიან ფარდობითი მაჩვენებელია, რადგან სხვადასხვა პროცესორზე სხვადასხვა შედეგი იქნება.

როგორ შევამციროთ HTTP მოთხოვნების რაოდენობა?
1.1. საიტის დიზაინში ხშირად გამოიყენება პატარა ილუსტრაციები, ხატულები, სხვადასხვა გრაფიკული ელემენტები. მათი უმეტესობა ისეთი სახით გვხვდება საიტზე, რომ შესაძლებელია css-ში აღვწეროთ და css sprite-ის სახით შევინახოთ: ყველა ილუსტრაცია არის ფაილში და შემდეგ css-ის საშუალებით ”კოორდინატების” მითითებით გამოვაჩენთ ილუსტრაციის ნაწილს.

მაგალითად, Facebook-ის სპრაიტი ასე გამოიყურება:

Facebook icons

FB icons

სპრაიტის შექმნის დროს ჯობია რაც შეიძლება ახლოს იყოს ფაილები განლაგებული, რომ ფაილის ზომა დიდი არ გამოვიდეს. სურათის ოპტიმიზაციაზე შემდეგ უფრო დეტალურად დავწერ.

ამ პროცესის ავტომატიზაციისთვის არსებობს რამდენიმე საიტი, მაგრამ მე ხელით მირჩევნია ხოლმე დალაგება და ზომების აღება.

1.2. HTTP მოთხოვნების რაოდენობა ასევე შეიძლება შევამციროთ css და javascript-ის ფაილების გაერთიანებით. მართალია, ხშირ შემთხვევაში კონკრეტულ გვერდზეა დამოკიდებული, თუ რომელი სკრიპტის და სტილის ფაილებს გამოიყენებს, მაგრამ მაინც შეგვიძლია გამოვარჩიოთ ისეთი ნაწილები, რომელიც ყველასთვის საჭიროა და შესაბამისად გავაერთიანოთ ფაილები.

მეორეს მხრივ ფაილების გაერთიანება სხვა პრობლემას ქმნის. თუ პარალელურად ახერხებს ბრაუზერი ჩატვირთვას, არ იქნება კარგი, ყველაფრის ერთ პროცესად ჩატვირთვა ვაიძულოთ. თანაც მას მოსდევს კიდევ ერთი გვერდითი ეფექტი. მაგალითად, iphone, სადაც iOS 3.1.3 ვერსიის სისტემა აქვს, ბრაუზერის კეშში ისეთ კომპონენტებს ინახავს, რომელიც 25.6 კბ-ს არ აღემატება შეკუმშვის გარეშე.

კომპონენტების ჩამოტვირთვის გასაპარალელებლად არსებობს სკრიპტი head.js, რომელიც თავის თავზე იღებს ჩვენი სკრიპტების ორგანიზებას და მათ ჩატვირთვას. ბიბლიოთეკა თავისი სიმარტივით გამოირჩევა და საკმაოდ გავრცელებულია. ბევრმა ჩაატარა შესამოწმებელი ტესტები (მაგალითად, Fero Novak-ის ბლოგზეა პოსტი ამის შესახებ) და უმეტეს შემთხვევაში მართლაც დიდი პროცენტით ასწრაფებს გვერდის ჩატვირთვას, თუმცა ზოგს პრობლემა აქვს მისი სხვა სკრიპტებთან ერთად გაშვების დროს.

მე პირადად არ გამომიცდია და თქვენ თუ იყენებთ ამ სკრიპტს, სიამოვნებით მოვისმენდი შთაბეჭდილებებს.

1.3. მოვერიდოთ Frame-ებს, რამდენადაც შეიძლება. თუ ორი frame გვაქვს, მინიმუმ სამი http მიმართვა იგზავნება – ერთი frameset-ის შიგთავსის წამოსაღებად და ორი მასში არსებული frame-ებისთვის.

2. Content Delivery (Distribution) Network-ის გამოყენება
CDN კომპიუტერების ქსელია, სადაც ერთი და იგივე ინფორმაციის ასლები ინახება. სერვერები ფიზიკურად სხვადასხვა ადგილებშია განლაგებული და როცა მომხმარებელი ინფორმაციის წამოსაღებად მიმართავს სისტემას, მოთხოვნას უპასუხებს მომხმარებელთან ქსელის თვალსაზრისით ყველაზე ახლოს მყოფი სერვერი.

დიდ ინტერნეტ კომპანიებს ხშირად საკუთარი CDN ქსელები აქვთ. თუ აპლიკაციას ძალიან ბევრი მომხმარებელი ჰყავს და სწრაფქმედება მნიშვნელოვანია, მან შეიძლება ასეთი ქსელის პროვაიდერების სერვისი გამოიყენოს (მაგალითად, Amazon CloudFront).

გუგლის CDN ქსელით ჩვენც შეგვიძლია ვისარგებლოთ – Google Libraries API ჯავასკრიპტის გავრცელებულ open-source ბიბლიოთეკებს ინახავს. მაგალითად, თუ საიტზე jQuery გვჭირდება, შეგვიძლია ჩვენი ჰოსტის მაგივრად სკრიპტი გუგლის CDN-დან წამოვიღოთ. ასეთ ჩატვირთვას რამდენიმე უპირატესობა აქვს:

  1. იქიდან გამომდინარე, რომ ფაილი CDN ქსელშია, ის მომხმარებლის ბრაუზერამდე მოკლე დროში მიაღწევს;
  2. რადგან სკრიპტი საიტისგან განსხვავებულ დომენზეა, ბრაუზერი მას სხვა რესურსების პარალელურად ჩატვირთავს;
  3. იგივე მისამართს სხვა ათასობით საიტი იყენებს ბიბლიოთეკის ჩასატვირთად. თქვენი საიტის მომხმარებელს დიდი ალბათობით ფაილი უკვე ბრაუზერის კეშში ექნება და წამოღება აღარ დასჭირდება.

3. CSS და Javascript კოდები გარე ფაილებში
სასურველია, რომ css სტილები და ჯავასკრიპტის კოდი რამდენადაც შეიძლება გარე ფაილებში იქნას გატანილი. როდესაც, მაგალითად, html ფაილში inline სტილებს ვწერთ, ამით ფაილის ზომასაც ვზრდით. გარე ფაილებს ბრაუზერი კეშში ინახავს. გვერდზე მეორედ შესვლის დროს ის მათ კეშიდან აიღებს და ხელმეორედ არ მიმართავს ვებ სერვერს.

4. Expires ჰედერი სტატიკურ ფაილებზე
როდესაც რაიმე რესურსზე ვგზავნით HTTP მოთხოვნას, ვებ სერვერი HTTP პასუხს აყოლებს Expires ჰედერს, რომელიც ბრაუზერს აცნობებს, რამდენი ხანი შეიძლება შეინახოს რესურსი თავის კეშში. ამ ჰედერს ითვალისწინებენ როგორც ბრაუზერები, ისე პროქსი სერვერები; რესურსებს შესაბამისად აკეშირებენ და მომხამრებელს ხშირად აღარ უწევს მათი ხელახლა წამოღება სერვერიდან.

შეგვიძლია ვებ სერვერი ისე დავაკონფიგურიროთ, რომ გარკვეულ ფაილებზე ან ტიპებზე შესაბამის Expires ჰედერში შორი მომავლის თარიღი მიუთითოს (თუმცა ერთ წელს არ უნდა გადააჭარბოს HTTP / 1.1 სპეციფიკაციის მიხედვით).

ამ მეთოდს უარყოფითი მხარე იგივე აქვს, რაც დადებითი. ბრაუზერი და პროქსი სერვერი ფაილებს არ წამოიღებს სერვერიდან, მაშინაც კი, თუ მათ განვაახლებთ, ამიტომ ასეთ განახლებებზე ფაილის სახელების შეცვლა იქნება საჭირო.

თუ აპაჩის ვებ სერვერს იყენებთ, mod_expires ან mod_header-ს მოდულების კონფიგურირება ამ ბმულზე შეგიძლიათ ნახოთ: Using a far future expires header

5. CSS და Javascript ფაილების ზომის შემცირება
სტილების და ჯავასკრიპტის კოდის წერის დროს მნიშვნელოვანია, რომ კოდი სუფთად და დალაგებულად დაიწეროს. ეს პროგრამისტს მის მოდიფიკაციებს და დროთა განმავლობაში შენახვას გაუადვილებს, მაგრამ ბრაუზერისთვის არ აქვს მნიშვნელობა რა განლაგებით ეწერება ტექსტი ფაილში. არსებობს ბევრი სხვადასხვა მინიმიზატორი, რომელიც ჩვენი კოდიდან ცარიელ ადგილებს წაშლის, ლოკალურ ცვლადებს სახელებს დაუპატარავებს და ა.შ.
რაც არ უნდა გასაკვირი იყოს, ასეთი მინიმიზაციის შედეგად ხშირად კოდი ძალიან მცირდება ზომაში.
მაგალითად jQuery-ის 1.7 ვერსიის სრული სახით მოცემული ფაილი 229 კილობაიტია, როცა მინიმიზირებული და შემდეგ gzip-ით შეკუმშული ფაილი მხოლოდ 31 კბ.

6. ტექსტური ფაილების შეკუმშვა Gzip მეთოდით
ტექსტური ფაილების ზომა დაზიპვის შემდეგ ძალიან მცირდება – 10-ჯერ, 20-ჯერ..
კარგი იქნებოდა, რომ ბრაუზერთან დაზიპული ფაილები იგზავნებოდეს, რაც ცხადია ბევრად სწრაფად მივიდოდა. სწორედ ამას აკეთებს ვებ სერვერი, თუ შესაბამისად დავაკონფიგურირებთ. ის კუმშავს ფაილს და უგზავნის ბრაუზერს, რომელიც ზიპიდან სრულ ფაილს აღადგენს და დაარენდერებს.
მთელი პროცესი ორი ძირითადი ნაბიჯისგან შედგება:

  1. თუ ბრაუზერს შეუძლია შეკუმშულ ფაილთან გამკლავება, ის ვებ სერვერთან გაგზავნილ მოთხოვნას შემდეგი სახის ჰედერს აყოლებს: Accept-Encoding: gzip, deflate
    gzip და deflate შეკუმშვის მეთოდებია. ბევრ საიტზე წერია, რომ gzip ყველაზე კარგი არჩევანია, მაგრამ ტესტების მიხედვით მე პირადად ვერ მივხვდი, რატომ არის სხვებზე ბევრად უკეთესი. ტესტები და შედარება შეგიძლიათ ამ ბმულზე ნახოთ: Compression Tests
  2. ასეთი ინფორმაციის მიღების შემდეგ, თუ სერვერს შეუძლია ფაილების შეკუმშვა, ის ბრაუზერს შეკუმშულ ფაილს დაუბრუნებს და ამას Content-Encoding: gzip ჰედერით შეატყობინებს, წინააღმდეგ შემთხვევაში ჩვეულებრივ გაგზავნის პასუხს.

ასეთი სახის შეკუმშვა უმჯობესია მხოლოდ ტექსტურ ფაილებზე გამოვიყენოთ, და არა, მაგალითად, სურათებზე ან pdf ფაილებზე, რადგან ისინი ისედაც შეკუმშულ ფორმატშია და ფაილის ზომა შეიძლება პირიქით გაიზარდოს.

Apache-ს ვებ სერვერის შემთხვევაში კონფიგურაციის ინსტრუქცია შეგიძლიათ ნახოთ საიტზე: How To Optimize Your Site With GZIP Compression

იმის გაგება, შეკუმშული მოდის თუ არა სერვერიდან ფაილები, პასუხის ჰედერებში შეიძლება. ან მაგალითად Http compression test საიტზე.

7. სურათების ფორმატები და შეკუმშვა
პირველ რიგში, ალბათ ისედაც არავინ აკეთებს, მაგრამ მაინც აღვნიშნავ, რომ სურათები html გვერდზე გამოყვანის დროს არ უნდა დავაპატარაოთ, არამედ სერვერზე უნდა გვქონდეს ის შესაბამის ზომებში შენახული და გვერდზე მხოლოდ საჭირო ზომის სურათი ჩავტვირთოთ.

სურათის ზომა მნიშვნელოვნად არის დამოკიდებული იმაზე, თუ როგორ ფორმატში შევინახავთ. ამის შესარჩევად უნდა ვიცოდეთ რომელი ფორმატი როგორი სახით ინახავს მას.
Clever PNG optimization techniques
Clever JPEG optimization techniques

ზოგადად მე ასეთ წესს ვიყენებ, რომ თუ ფოტოს მსგავსი გამოსახულებაა, jpg-თი ვკუმშავ, ხოლო გრადაციებს ან ცოტა ფერიან, მარტივ გამოსახულებებს png-ში და gif-ში ვამოწმებ. შედეგს ვადარებ და ვირჩევ.

თურმე კიდევ შეიძლება მიღებული png სურათის გაუმჯობესება. მაგალითად, არსებობს ასეთი საიტი Smush it! რომელიც ატვირთულ ფაილებს დანაკარგის და ხარისხის გაფუჭების გარეშე აოპტიმიზირებს.

ჯეოლიმპის შემთხვევაში გავაერთიანე წვრილ წვრილი პატარა ზომის ჯავასკრიპტის ფაილები. ზოგან შეუკუმშავს ვიყენებდი და გამოვასწორე..
ძირითადი css ფაილი მინიმიზაციის შემდეგ 27%-ით შემცირდა ზომაში და 53.2კბ-დან 38.5კბ-ზე ჩამოვიდა.

.png ფაილები smushit საიტზე გატარების შემდეგ ჯამში 140.76 კბ-ით ანუ 49.68%-ით შემცირდნენ.

განახლებულ საიტზე იგზავნება 29 http მოთხოვნა, ჯამში 173 კბ და მთლიანი ჩატვირთვის დრო საშუალოდ 0.8 – 1.0 წამია.

ანუ, კეშირების გარეშე ასეთი სურათია

ძველი ახალი გაუმჯობესება
მოთხოვნები 42 29 30.9%
ფაილების ზომა 307.9 173 43.81%
ჩატვირთვის დრო 1.2 – 2.3 0.8 – 1.0 ~40%

აქ gzip-ით შეკუმშვა არ შედის, იმიტომ, რომ მანამდეც ჩართული იყო ის, მაგრამ შეკუმშვის გარეშე 173 კილობაიტის ნაცვლად დაახლოებით 432 კილობაიტი გამოვიდოდა.

იმავე გვერდის refresh-ის შემდეგ კეშირების წყალობით მხოლოდ 15 კილობაიტი იტვირთება და onload-მდე დრო საშუალოდ 0.4 წამია.

დრო ორჯერ შევამცირეთ, მაგრამ სამწუხაროდ ეს საკმარისი არ არის რომ იგივე დროში ორჯერ მეტ მომხმარებელს მოემსახუროს საიტი. ამისთვის სერვერის მხარეს იქნება ცვლილებები საჭირო.

Vanilla Cage – Breaking the Cage

არ შემიძლია, პოსტი არ მივუძღვნა Vanilla Cage-ის ევროვიზიის საკონკურსო სიმღერას <3 რადგან ვამაყობ ჩემი დის შესრულებით (drums). წარმატებებს გისურვებთ გოგოებო :) )

დამოუკიდებელი ტრანზაქციები

ტრანზაქციების ქვეშ ვგულისხმობ მონაცმებთა ბაზის ტრანზაქციებს, რომლებიც სხვადასხვა ოპერაციებს აერთიანებენ და ACID თვისებების დაცვით ბაზაში არსებული მონაცემების კორექტულობას უზრუნველყოფენ. ასეთი ტრანზაქციების მხარდაჭერა დღეს მგონი ყველა გავრცელებულ რელაციურ მონაცემთა ბაზას აქვს.

როდესაც ისეთი ოპერაციების ჩატარება გვინდა ბაზის მონაცემებზე, ან მრავალი ფუნქციის და პროცედურის გამოძახება, რომლებიც ერთმანეთზეა დამოკიდებული და თუ ერთგან მაინც რაიმე შეცდომა მოხდა, ყველაფერი უკან უნდა დაბრუნდეს, ყველა insert, update და delete უნდა გაუქმდეს, ასეთ შემთხვევაში ამ ყველაფერს ერთ ტრანზაქციაში მოვაქცევთ. მუშაობის დასრულების ბოლოს კი, იმის მიხედვით ყველაფერი წარმატებით შესრულდა თუ არა, ტრანზაქციას დავა-commit-ებთ, ან rollback ბრძანებით გავაუქმებთ. ამისთვის საჭიროა, რომ მთელი პროცესის განმავლობაში შეცდომის ჰენდლინგი სწორად გავაკეთოთ, მოვიფიქროთ სად დავიჭიროთ და დავამუშაოთ ხოლმე exception და სად გავუშვათ სულ გარეთ, მანამ სანამ სულ გარე ფუნქციაში არ გავა.

ანუ განვსაზღვროთ როდის უნდა დასრულდეს პროცესი  წარმატებით და როდის უნდა შეჩერდეს. შესაბამისად, ამ კონკრეტული პირობების შემთხვევაში ან სამუდამოდ შევინახავთ ცვლილებებს, ან გავაუქმებთ.

საქმე იმაშია, რომ ზოგჯერ გვჭირდება პროცესის შიგნით ზოგიერთი ნაწილი უპირობოდ დავაკომიტოთ, იმის მიუხედავად ტრანზაქცია წარმატებით დასრულდა თუ არა. მაგალითად, თუ გზადაგზა სხვადასხვა ფუნქციაში მომხდარ შეცდომებს ცხრილში ვინახავთ, გვინდა რომ ისინი ყველა შემთხვევაში შენახული იყოს. მაგრამ როდესაც მათ შესანახად კომიტს გავუშვებთ, ის იმ ტრანზაქციის ფარგლებში შესრულებულ ყველა ოპერაციას დააკომიტებს, ამიტომ სხვა გზა უნდა ვიპოვოთ.

თუ ასეთი სახის ამოცანა ორაკლში უნდა გადავჭრათ, მაშინ შეგვიძლია ზოგიერთი ტრანზაქცია მთავარი ტრანზაქციიდან დამოუკიდებლად გავუშვათ. ამისათვის ჩვენი ქვეპროგრამის declaration სექციაში (სადაც სხვადასხვა ცვლადების ან ქვეპროგრამების აღწერა ხდება), უნდა ჩავუწეროთ pragma autonomous_transaction;

pragma კომპილატორის დირექტივაა. მისი საშუალებით შეგვიძლია კომპილატორს ინფორმაცია მივაწოდოთ და ამით ქვეპროგრამის მუშაობის გზა შევცვალოთ. ორაკლში ასეთი კიდევ სამი სახის ინსტრუქცია არსებობს exception_init, restrict_references, serially_reusable, თუმცა მათ ახლა არ ჩავუღრმავდები.

მაგალითად, თუ ჩვენს log_error პროცედურაში (რომელიც ზევით არსებულ პროცესის ნაწილია და გზადაგზა შეცდომებს ინახავს ცხრილში) ჩავამატებთ autonomous_transaction პრაგმას, მასში გამოძახებული commit და rollback ბრძანებებს არ ექნება გავლენა მთავარ ტრანზაქციაზე და მხოლოდ ქვეპროგრამის ცვლილებებს შეინახავს. ცხადია, ეს დამოუკიდებლობა ვრცელდება მთავარ ტრანზაქციასთან დაკავშირებულ რესურსებსა და lock-ებზეც.

pragma autonomous_transaction-ით შეგვიძლია ანალოგიური შესაძლებლობა მივცეთ ორაკლის ტრიგერს, რომელშიც ჩვეულებრივ არ შეიძლება დაკომიტება, რადგან ის აღმძვრელ ოპერაციაზეა დამოკიდებული (ანუ რის გამოც მოხდა ტრიგერის გამოძახება). შესაბამისად, თუ ეს უკანასკნელი დაკომიტდა, ჩვეულებრივი ტრიგერიც დაკომიტდება და ასევე როლბექის შემთხვევაში.

დამოუკიდებელ ტრიგერში ამის გარდა შესაძლებელი გახდება DDL ბრძანებების შესულება (ცხრილის შექმნა, ცვლილება, ა.შ.).

MS SQL მონაცემთა ბაზაში, როგორც ჩანს, არ არსებობს ასეთი დამოუკიდებელი ტრანზაქციების ანალოგი (ყოველ შემთხვევაში ძველ ვერსიებში არ ჰქონდათ მისი მხარდაჭერა), მაგრამ არსებობს შემოვლითი გზები, რითიც ასეთი მიზნის მიღწევა შეიძლება.

MySQL-საც არ აქვს დამოუკიდებელი ტრანზაქციების ცნება, თუმცა აქ საქმე ცოტა სხვაგვარადაა. ზევით ნახსენებ ბაზებისგან განსხვავებით MySQL-ში საერთოდ ტრანზაქციების არსებობა მის storage engine-ებზეა დამოკიდებული. ასე რომ, თუ ვთქვათ შეცდომების ლოგირება გვინდა რომელიმე ცხრილში, შეგვიძლია იმ ცხრილისთვის MyISAM ძრავი გამოვიყენოთ რომელსაც არ აქვს ტრანზაქციების მხარდაჭერა, და როლბექის დროს ამ ცხრილში არსებულ ჩანაწერებს არაფერი მოუვა.

უდიდესი საერთო ქვემიმდევრობის პოვნის ამოცანა

წინა პოსტში მაგისტრატურის გამოცდა ვახსენე და დღეს მისთვის მოსამზადებლად მეორე ალგორითმს გავიხსენებ. ფსევდო კოდები და მაგალითები ისევ ”Introduction to Algorithms” წიგნიდან ავიღე :)

”უდიდესი საერთო ქვემიმდევრობის” (Longest Common Subsequence) პოვნა დინამიური პროგრამირების კიდევ ერთი კლასიკური ამოცანაა. ვთქვათ მოცემულია მიმდევრობები:

X = (A, B, C, B, D, A, B)
Y = (B, D, C, A, B, A)

ამ შემთხვევაში მათი ერთ-ერთი უდიდესი საერთო ქვემიმდევრობაა მიმდევრობა (B, C, B, A)

ცოტა უკეთ განვსაზღვროთ, თუ რა არის ქვემიმდევრობა:
ვთქვათ მოცემულია მიმდევრობა X = (x1, x2, … , xm).
Z = (z1, z2, …. , zk) არის მისი ქვევმიმდევრობა, თუ არსებობს (i1,i2..,ik) X-ის ინდექსების ისეთი მკაცრად ზრდადი მიმდევრობა, რომ ყველა j=1,2,… k მნიშვნელობისთვის სრულდებოდეს ტოლობა xij = zj.

არც ამ ამოცანის ამოსახსნელად გამოგვადგება ყველა ვარიანტის სრული გადარჩევა, რადგან ექსპონენციალურ დროს მოითხოვს და დიდი მიმდევრობებისთვის პრაქტიკულად ვერ მივიღებთ პასუხს.
თუ დავაკვირდებით მიმდევრობებს, გამოჩნდება, რომ დიდი ამოცანა შეგვიძლია დავყოთ ქვეამოცანებად, სადაც ქვეამოცანა შეესაბამება მოცემული მიმდევრობების პრეფიქსთა წყვილებისთვის უდიდესი საერთო ქვემიმდევრობის პოვნას.

ინტუიციურია, მაგრამ მაინც განვსაზღვროთ მიმდევრობის ”პრეფიქსის” ცნება. თუ მოცემული გვაქვს მიმდევრობა X = (x1, x2, … , xm), მისი i-ური პრეფიქსი იქნება X-ის პირველი i ცალი ელემენტისგან შემდგარი მიმდევრობა, ანუ Xi = (x1, x2, … , xi). რა თქმა უნდა i4 = (A,B,C,B). X0 ცარიელი მიმდევრობაა.

სანამ ამოცანის ამოხსნაზე გადავალთ ერთი პატარა თეორემა უნდა დავამტკიცოთ.
თეორემა:
მოცემულია X = (x1, x2, … , xm) და Y = (y1, y2, … , yn) მიმდევრობები. ხოლო Z = (z1, z2, … , zk) არის ამ მიმდევრობების უდიდესი საერთო ქვემიმდევრობა.
1. თუ xm = yn, მაშინ zk = xm = yn და Zk-1 არის Xm-1 და Yn-1 პრეფიქსების უსქ (უდიდესი საერთო ქვემიმდევრობა).
2. თუ xm !=yn, მაშინ zk != xm-დან გამომდინარეობს, რომ Z არის Xm-1 და Yn პრეფიქსების უსქ.
3. თუ xm !=yn მაშინ, zk != yn-დან გამომდინარეობს, რომ Z არის Xm და Yn-1 პრეფიქსების უსქ.

დამტკიცება საკმაოდ მარტივია.
1. დავუშვათ საწინააღმდეგო და ვთქვათ, რომ zk != xm. რადგან xm = yn, Z-ს შეგვიძლია მივამატოთ xm ელემენტიც. ამ შემთხვევაში კი გამოვა რომ უდიდესი საერთო ქვემიმდევრობა Z-ზე გრძელია და წინააღმდეგობას მივიღებთ. მოკლედ რომ ვთქვათ, თუ მოცემული მიმდევრობების ბოლო ელემენტები ერთნაირია, ის ელემენტი აუცილებლად იქნება უსქ-ის ბოლო ელემენტი.

დებულების მეორე ნაწილიც იგივე პრინციპით დავამტკიცოთ. ვთქვათ Zk-1 არ არის Xm-1 და Yn-1 პრეფიქსების უსქ. მაშინ იარსებებს რაიმე უსქ W, რომლის სიგრძეც k-1-ზე მეტია. მას რომ მივამატებთ xm = yn ელემენტს, ის k-ს გადააჭარბებს. ეს წინააღმდეგობაა.

2. რომ არსებობდეს Xm-1 და Y-ის უდიდესი საერთო ქვემიმდევრობა – W, რომლის ელემენტების რაოდენობა k-ზე მეტია, ის აგრეთვე იქნებოდა X-ის და Y-ის უსქ და თან Z-ს გადააჭარბებდა სიგრძეში. მოცემულობის მიხედვით კი Z არის X-ის და Y-ის უსქ.

3. მეორე პუნქტის ანალოგიურად მტკიცდება.

ამ თეორემიდან კარგად ჩანს რომ ორი მიმდევრობის უდიდესი საერთო ქვემიმდევრობა თავის თავში შეიცავს ამ მიმდევრობების პრეფიქსების უდიდეს საერთო ქვემიმდევრობას და მის საპოვნელად რეკურენტული ფორმულა შეგვიძლია შევადგინოთ.

თუმცა ცხადია, რომ პირდაპირი რეკურსია სრულ გადარჩევას გააკეთებს, რადგან მისი განშტოებები ძალიან ხშირად ერთსა და იმავე ქვეამოცანას ამოხსნიან. ამიტომ, როდესაც დავადგენთ უდიდეს ქვემიმდევრობის სიგრძეს თითოეული პრეფიქსისთვის, დავიმახსოვროთ ის და მეორედ აღარ მოვძებნოთ იგივე პრეფიქსის უსქ.

კონკრეტულად, შემოვიღოთ რიცხვითი ტიპის ორგანზომილებიანი მასივი c და მის [i][j] უჯრაში შევინახოთ Xi და Yj პრეფიქსების უსქ-ის სიგრძე.

როცა i ან j ნოლის ტოლია მაშინ c[i][j]-ის მნიშვნელობა ნოლის ტოლი იქნება რადგან საერთო ქვემიმდევრობაც ცარიელი გამოვა პრეფიქსის მსგავსად.

როცა i, j >0 და xi = yj, ეს ნიშნავს რომ Xi და Yi მიმდევრობების უსქ-ის სიგრძე ერთით მეტია ვიდრე Xi-1 და Yi-1 მიმდევრობების უსქ-ის სიგრძე, რადგან ერთი კიდევ ერთი საერთო ელემენტი დაემატა.

როცა i,j>0 და xi != yj, ეს ნიშნავს რომ უსქ-ის სიგრძე იგივეა რაც Xi-1 და Yj პრეფიქსების ან Xi და Yj-1 პრეფიქსების უსქ-ს ჰქონდა. ამიტომ ამ ორიდამ მაქსიმალური სიგრძისა ავირჩიოთ და მისი მნიშვნელობით შევავსოთ c[i][j] უჯრა.

საბოლოოდ ასეთი რეკურენტული ფორმულა მივიღეთ:

თუმცა საბოლოო პასუხი არა მხოლოდ უდიდესი საერთო ქვემიმდევრობის სიგრძეა, არამედ თვითონ ქვემიმდევრობა. ამიტომ შემოვიღოთ c-ის ზომის კიდევ ერთი დამატებითი ორგანზომილებიანი მასივი b, რომლის [i][j] უჯრაში შევინახავთ ხოლმე თუ რომელი მეთოდით შევავსეთ c[i][j].

ფსევდო კოდი ასე გამოიყურება:

კოდში კარგად ჩანს რომ ორი ჩადგმული ციკლი მუშაობს. მათგან ერთი m, ხოლო მეორე n ცალ იტერაციას აკეთებს. თითო უჯრის მნიშვნელობის გამოთვლა O(1) დროში ხდება. ანუ ალგორითმი O(mn) დროში მუშაობს, სადაც m და n მოცემული მიმდევრობების სიგრძეებია.

ვნახოთ როგორ შეავსებს ეს ფუნქცია მასივებს ჩვენს მაგალითზე დაყრდნობით:

მხოლოდ ისღა დაგვრჩენია რომ ამ მონაცემებიდან უდიდესი საერთო ქვემიმდევრობა აღვადგინოთ. ამისთვის პატარა რეკურსიული ფუნქცია გამოვიყენოთ.

ეს ფუნქცია O(m + n) დროში მუშაობს, რადგან ის ყოველ ჯერზე მინიმუმ ან i-ის ამცირებს ერთით, ან j-ის, ან საერთოდ ორივეს ერთად.

შევაფასოთ გამოყენებული მეხსიერებაც. ამ ალგორითმში ორი ცალი m x n ზომის მასივი გვაქვს, ამიტომ O(mn) მეხსიერებას ვიყენებთ. შეიძლება b მასივი საერთოდ გამოვტოვოთ, თუმცა მეხსიერების O(mn) შეფასება არ შეიცვლება.
საქმე იმაშია, რომ c[i][j] უჯრის მეზობელ c[i-1][j-i], c[i][j-1], c[i-1][j] უჯრებს თუ დავათვალიერებთ, მარტივად მივხვდებით რომელ მათგანზე დაყრნობით მივიღეთ c[i][j] მნიშვნელობა და აღარ დაგვჭირდება ამის b მასივში შენახვა.

განახლება კომენტარიდან:

Zuba Dalama:
ერთ-ერთი “real world” მაგალითია diff: http://en.wikipedia.org/wiki/Diff

ვისაც აინტერესებს თუ როგორაა რეალიზებული იხ. google–ის opensource პროექტი (წარმოდგენილია შემდეგ ენებზე: Java, JavaScript, Dart, C++, C#, Objective C, Lua და Python) http://code.google.com/p/google-diff-match-patch/
და მისი დოკუმენტაცია: http://neil.fraser.name/writing/diff/.

მატრიცათა მიმდევრობის გადამრავლების ამოცანა

გადავწყვიტე წელს უნივერსიტეტში დავბრუნდე მაგისტრანტის სახით, მაგრამ ამისთვის ჯერ გამოცდა უნდა ჩავაბარო რამდენიმე დღეში. საკითხების სიაში დინამიური პროგრამირების კლასიკური ამოცანებიც შედის, რომელთაგან ერთ-ერთი ”მატრიცათა მიმდევრობის გადამრავლების” ამოცანაა (Matrix chain multiplication). ამ პოსტში მისი ამოხსნის შესახებ დავწერ. მაგალითები და ფსევდო კოდები Introduction to Algorithms წიგნიდან ავიღე.

სანამ ამოცანის დასმაზე გადავალ, მოკლედ გავიხსენოთ მატრიცების გადამრავლება. ეს არის ბინარული ოპერაცია, როდესაც ორ მატრიცაზე ოპერაციების ჩატარებით ვიღებთ ახალ, მესამე მატრიცას.

აქ ვხედავთ, რომ გამრავლებისთვის აუცილებელია პირველი მატრიცის სვეტების რაოდენობა მეორე მატრიცის სტრიქონების რაოდენობას ემთხვეოდეს. შესაბამისად, თუ პირველი მატრიცის განზომილებებია p და q, ხოლო მეორე მატრიცის – q და r, მათი გადამრავლებისთვის pqr ცალი წრფივი გამრავლების ოპერაცია დაგვჭირდება.

მატრიცების გამრავლების ოპერაცია ასოციაციურია, ანუ (AB)C = A(BC), მაგრამ არა კომუტატური – AB ნამრავლის შედეგი შეიძლება არ იყოს BA ნამრავლის შედეგად მიღებული მატრიცის ტოლი.

ვთქვათ მოცემულია A1,A2,A3 მატრიცების მიმდევრობა, სადაც A1 მატრიცის განზომილებებია 10 x 100, A2 – 100 x 5,   A3 – 5 x 50. იმის მიხედვით, თუ რა თანმიმდევრობით გავამრავლებთ მატრიცებს, სხვადასხვა რაოდენობის ოპერაცია დაგვჭირდება, თუმცა ასოციაციურობის გამო საბოლოო შედეგი ყოველთვის ერთი და იგივე მატრიცა იქნება. მაგალითად:


Continue reading

ბიტური ოპერაციები ორაკლში

დღეს ზედმეტი მოვინდომე და ბაზაში მონაცემების ბიტმასკების სახით შენახვა გადავწყვიტე, რომელიც მერე ისევ თვითონ ბაზას უნდა დაემუშავებინა. შემდეგ ვნახე, რომ ორაკლში არ არის რეალიზებული ბიტური ოპერაციები და არის მხოლოდ ერთი – bitand() ფუნქცია, რომელიც ბიტურ “and”-ს აკეთებს რიცხვებზე.
“or” და “xor”-ის მიღებაც ამ ფუნქციის გამოყენებით მარტივად შეიძლება. მაგალითად a და b რიცხვებზე “or” ოპერაცია გამოვა

a + b - bitand(a,b)

ხოლო “xor”-ს მივიღებთ თუ კიდევ ერთხელ გამოვაკლებთ:

a + b - bitand(a,b) - bitand(a,b)

არც ბიტური წაძვრები (shift >>) აქვს, მაგრამ მაგის ჩანაცვლება 2-ზე გამრავლებით და გაყოფით შეიძლება (ეგ ალბათ ჯობია big endian და little endian ვარიანტებზე წინასწარ შემოწმდეს ხოლმე, რომ მეორე მხარეს არ წაძრას ვიდრე ველოდებით).

MySQL-ში არ დამჭირვებია, მაგრამ ეხლა შევხედე და ჰქონია პირდაპირ &, |, ~, ^, <<, >> და თან bit_count() ფუნქციაც, რომელიც ჩართულ ბიტების რაოდენობას აბრუნებს. მათი გამოყენება პირდაპირ sql ქვერებშიც შეიძლება.

ბოლოს დამაინტერესა და სხვა ბაზებსაც გადავწვდი. PostgreSQL-ში უკანასკნელის მსგავსად ყველა არის რეალიზებული, MS SQL-ში კი მხოლოდ and, or და xor არიან.

პრინციპში დიდი გამოყენება ალბათ არ აქვთ, მაგრამ ზოგ შემთხვევაში ძალიან მოსახერხებელი გამოდის. მაგალითად, თუ ობიექტთან დაკავშირებული სტატიკური (თუნდაც არასტატიკური) პატარა სიმრავლის შენახვაა საჭირო, ანუ ამ ობიექტისთვის სიმრავლეში რომელი ელემენტებია ”ჩართული” და რომელი არა.

ფაილური სისტემის შეზღუდვები

ერთ-ერთი საიტის კოდში შემხვდა hack ლინუქსისთვის, რომელიც ფაილური სისტემის შეზღუდვის გათვალისწინების გამო იყო ჩამატებული. მივხვდი, აქამდე არასდროს მიფიქრია ასეთ წინააღმდეგობებზე და გადავწყვიტე ფაილური სისტემების აღწერისთვის მიმეძღვნა პოსტი, თუმცა იმდენნი არიან, ამ ერთი ჰეკით შემოვიფარგლები.

(ვიკიპედიაზე არის ქართული სტატიის ნაწილი მათ შესახებ, აქ კი სისტემების შედარების ცხრილებია)

კოდის ფრაგმენტი, რაც ზევით ვახსენე, საიტზე სურათების ატვირთვას ეხებოდა. როგორც ჩანს ლინუქსის ფაილურ სისტემებს (ext2/ext3) შეზღუდვა აქვთ, რომ ერთ საქაღალდეში 32000-ზე მეტი ქვე საქაღალდის შენახვა არ შეიძლება. ანუ თუ მაგალითად, მომხმარებლის ფოტოების შესანახად ერთი საქაღალდე გაქვთ გამოყოფილი და იქ თითოეული იუზერისთვის ქვესაქაღალდეს ქმნით, 32000-ზე მეტი იუზერის შექმნის შემთხვევაში ამ საქაღალდეებს ვეღარ შექმნის.

ერთი გამოსავალი ისაა, რომ ფაილური სისტემა შეცვალოთ (როგორც წერენ, ReiserFS-ს ასეთი შეზღუდვა არ აქვს და ასობით ათასს ფაილსსაც მარტივად უმკლავდება), მაგრამ თუ სხვა მხრივ ხელს არ გიშლით, ჩემი აზრით უკეთესია უბრალოდ სხვანაირად დაანაწილოთ ფაილები და ისე შეინახოთ.

მაგალითად, შეიძლება root ფოლდერში შეინახოთ საქალდეები სახელად 10000, 20000, 30000, ხოლო თითოეულში მაქსიმამუმ 10000 ცალი საქაღალდე რომელიც შესაბამისად არის გადანომრილი (1,2,3,.. ). როდესაც საჭირო იქნება იმის დადგენა თუ რომელ გარე საქაღალდეში უნდა მოიძებნოს თქვენი ფოლდერი, მისი სახელის 10000-ზე მთელი გაყოფით და შემდეგ ისევ 10000-ზე გამრავლებით გარე ფოლდერის სახელს მიიღებთ.

შეზღუდვები რომც არ იყოს, ზოგი ფაილური სისტემა არ არის ოპტიმიზირებული რომ ბევრ ფაილიან ფოლდერში სწრაფად ჩაამატოს, ამოშალოს ან წაიკითხოს ფაილები. მაგალითად ლინუქსის ext2 და ext3 დირექტორიების და ფაილების იერარქიას ბმული სიების სახით ინახავდნენ და შესაბამისად იქ ძებნას O(n) დრო სჭირდებოდა. ზოგ შემთხვევაში პრობლემა არაა, მაგრამ როცა, თუნდაც, კეშის მსგავსი სტრუქტურები იყო შესანახი, ძალიან ანელებდა ეს ყველაფერს. შემდეგ დაამატეს მარტივი ხის სტრუქტურა  - HTree, რომელიც ext3-ში შეიძლება ”ჩაირთოს”, ext4-ს კი default-ად აქვს უკვე. სხვა ფაილური სისტემებიდან ბევრი B-tree-ის მონაცემთა სტრუქტურას იყენებს სწრაფი მუშაობისთვის. ამაზე ალბათ მოგვიანებით დავწერ.

იქით მივდიოდი, რომ უკეთესი წარმადობისთვის ჩვენმა აპლიკაციამაც შეიძლება შეუწყოს ხელი ოპერაციულ სისტემას. უკვე რამდენგანმე შემხვდა ჰეშირებას გამოყენება ფაილების მენეჯმენტის დროს. მაგალითად, რომ აიღოთ ფაილის სახელის ჰეში 910a90401e7bdc0452eea5630, ამ ჰეშის პირველი რამდენიმე სიმბოლო გამოიყენოთ საქაღალდეების იერარქიისთვის, ხოლო დანარჩენი ფაილის სახელისთვის.

91\0a\90401e7bdc0452eea5630.jpg

კარგი ჰეშ ფუნქციის შემთხვევაში საქაღალდეები და ფაილები დაახლოებით თანაბრად განაწილდება და ფოლდერები არ გადაიტვირთება. არც ფაილის მოძებნა იქნება მძიმე.

მაგალითად,  Squid (web proxy cache) ინახავს კეშირებულ ფაილებს მსგავსი სახით.

Case sensitivity MySQL-ში

ორ ასოზე მეტის დაწერა ქართულად ვერ მოვახერხე.. :D

ბოლო დროს ერთ პრობლემაზე იმდენი დრო დავკარგე, რომ დამამახსოვრდა. სინამდვილეში არაკორექტული ლოგირების გამო უფრო იყო, მაგრამ ეს არაა ახლა მთავარი. MySQL-ში როცა რამეს აღვწერთ, თითქმის ყველაფერი case-insensitive არის, ანუ არ აქვს მნიშვნელობა ველის სახელს დიდი ასოებით დავწერთ თუ პატარით.

კონკრეტულად, არ აქვს მნიშვნელობა ველის, ფუნქციის, პროცედურის, ინდექსის და ალიასის შემთხვევაში. ტრიგერი ამ მხრივ გამონაკლისია.

ხოლო რაც შეეხება ცხრილის და ბაზის სახელებს – მათი ქეის სენსითივობა ოპერაციულ სისტემაზეა დამოკიდებული.

კი, ვერ მივხვდი ლინუქსში რატომ აღარ მუშაობდა :D

MySQL-ში მონაცემთა ბაზები დირექტორიებს შეესაბამება, ცხრილი კი მინიმუმ ერთ ფაილს ბაზის დირექტორიაში. ეს ნიშნავს რომ ვინდოუსის შემთხვევაში რეგისტრს მნიშვნელობა არ აქვს, იუნიქსის პლატფორმაზე კი აქვს.

შეიძლება lower_case_table_names პარამეტრის გამოყენებაც, თუმცა ამას ყველაფერი პატარა რეგისტრში გადაჰყავს და თუ მიგრაციას აკეთებთ, მაშინ არსებული ცხრილების და ბაზების სახელების კონვერტაცია იქნება საჭირო წინასწარ.

ჩემი სემინარი თსუ-ში 2: ვებ სერვისები (SOAP, REST)

:) როგორც ჩემს წინა პოსტში ვწერდი, წინა შაბათს – 22 იანვარს სტუდენტურ სემინარებზე ჩემი ”მოხსენება” იყო ვებ აპლიკაციის უსაფრთხოებაზე.

ეს არის დაპირებული ბმული პრეზენტაციაზე, ხოლო ეს არის თვითონ სემინარის შინაარსი. თუ ვინმე ნახავს და შენიშვნები ექნება ან ჩათვლის რომ ვიტყუები :D გთხოვთ, დამიკომენტარეთ და გავასწორებ.

ამ შაბათს კი (29 იანვარს ისევ 5 საათზე) ისე მოხდა, რომ ისევ მე ვკითხულობ სემინარს და თემა შეეხება ვებ სერვისებს.

ისევ დავიწყებ შედარებით საბაზო სტრუქტურით და შესავლით, ხოლო შემდეგ ვისაუბრებ SOAP და REST არქიტექტურის მქონე სერვისებზე.

ჩემი სემინარი თსუ-ში: ვებ აპლიკაციის უსაფრთხოება

რამდენიმე თვის წინ თბილისის სახელმწიფო უნივერსიტეტის კომპიუტერული მეცნიერების მიმართულების სტუდენტებმა ერთ საინტერესო წამოწყებას ჩაუყარეს საფუძველი : D

სტუდენტები ამზადებენ მათთვის სასურველ თემას და შემდეგ სხვა სტუდენტებს უტარებენ სემინარს.

მაგალითად აქამდე ჩატარდა სემინარები:

  • HTML5-სა და HTML4–ს შორის განსხვავება და ახალი API–ები (ნინო ლომინეიშვილი)
  • HTML5: Drag & Drop (მარიამ ხუნჯგურუა)
  • HTML5 Web Storage (შოთა ბაკურაძე, არჩილ ვარშანიძე)
  • Mobile Device Game Architecture (გიორგი აბელაშვილი)
  • Introduction to Android development (ირაკლი მანაგაძე)
  • Facebook for developers (დავით თვალჭრელიძე, გიორგი მაისურაძე)

სემინარების შესახებ ინფორმაცია ქვეყნდება ხოლმე facebook-ზე.

ამ შაბათს, ანუ 22 იანვარს (როგორც ყოველთვის 17:00 საათზე ბიოლოგების კორპუსის 421-ე აუდიტორიაში) იქნება ჩემი სემინარი :D

რომელიც ვებ აპლიკაციის უსაფრთხოებას შეეხება.

დავიწყებ სულ საბაზო სტრუქტურის მიმოხილვით, თუ როგორ არის ქსელში მოწყობილი კლიენტისა და ვებ აპლიკაციის ურთიერთობა და რატომ არის ისეთი, როგორიც არის (HTTP პროტოკოლი, HTTP request / response, სესიები).

ხოლო შემდეგ განვიხილავ რა საფრთხე შეიძლება იყოს სისტემის თითოეულ ფენაზე – ვებ სერვერის მხარეს, გზაში, კლიენტის მხარეს.

შევეხები თემებს: Injecting OS commands / SQL / scripting languages, Directory traversal, Remote file inclusion, Persistent / Non-persistent XSS attack, Cross-site tracing და საშიში HTTP მეთოდები,  DDoS attack, Brute force, და კიდევ რამდენიმე, თუ არ გამომაგდეს აუდიტორიიდან :D

ესეც ანონსი..

p.s. იმედი მაქვს ვინმე არ გადაწყვეტს დაამტკიცოს რომ აზრზე არ ვარ უსაფრთხოების და ხვალ ჩემი ბლოგი ცოცხალი დამხვდება :  D ჩვეულებრივი ვორდპრესია და ბეკაპების გარდა არაფრისთვის ვიწუხებ თავს.