მილიარდ დოლარიანი შეცდომა

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

პრეზენტაცია 1965 წელს გამოგონილი null reference-ს ეხებოდა, რომელიც მილიარდ დოლარიან შეცდომად შეაფასა მისმა შემქნელმა:

“I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.”

რეალურად, მე არ ვფიქრობ რომ თვითონ null reference-ის შექმნა იყო არასწორი, რადგან მის გარეშე ალბათ ძალიან ძნელი იქნებოდა თუნდაც OOP პროგრამირება, გაცილებით ზედმეტი ლოგიკის წერა და უარესი კომპრომისების და ბაგების მოვლა მოგვიწევდა. ამიტომ შეგვიძლია უფრო type checking სისტემებს დავაბრალოთ null-თან დაკავშირებული პრობლემები. ყოველ შემთხვევაში, ჰოარის პრეზენტაციიდანაც დაახლოებით ასეთი აზრი გამოვიტანე.

კონკრეტულად რომ ვთქვათ აი ასეთ პრობლემაზეა საუბარი (ორაკლის საიტიდან ავიღებ მაგალითს):
წარმოვიდგინოთ ჩანაწერი..

String version = computer.getSoundcard().getUSB().getVersion();

როცა ჯავაში აღვწერთ მეთოდს, მას ვუთითებთ დასაბრუნებელ ტიპს. რეალურად დასაბრუნებელი მნიშვნელობა არ არის ამ ტიპის, არამედ ერთ-ერთია სიმრავლიდან { null, ეს ტიპი }

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

String version = "UNKNOWN";
if(computer != null){
    Soundcard soundcard = computer.getSoundcard();
    if(soundcard != null){
        USB usb = soundcard.getUSB();
        if(usb != null){
            version = usb.getVersion();
        }
    }
}

თუმცა, როგორც წესი, უფრო ხშირად ვცდილობთ რომ ასეთი ჩანაწერი თავიდან ავირიდოთ. best practice-ების მიხედვით რეკომენდირებულია მეთოდი ისე ვწეროთ რომ მან არასდროს დააბრუნოს null, არამედ, ან რაღაცა ცარიელი ობიექტი მიიღოს შედეგად ან checked შეცდომა ისროლოს. ასე ნაწერი კოდი გაცილებით გვაზღვევს runtime-ში null pointer შეცდომებისგან.

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

ჯავაში ასე გამოვიდოდა getSoundcard მეთოდის სიგნატურა:

Optional<Soundcard> getSoundcard()

მერე გამოყენებისას უნდა დავწეროთ

Optional<Soundcard> optional = getSoundcard()
if (optional.isPresent()) {
    Soundcard soundcard = optional.get();
    …..
}

ან სხვა უფრო მოსახერხებელი მეთოდების გამოყენება შეიძლება – orElse, ifElse, ifPresent.. ა.შ.

ზოგ ენაში შემოიღეს მოკლე ჩანაწერიც, მაგალითად Swift-ში შეგხვდებოდათ ასეთი რამ:

var version = computer?.soundcard?.USB?.version

ამ ჯაჭვში ერთ ერთი მაინც თუ დააბრუნებს null-ს, მომდევნო getter-ების გამოძახება აღარ გაგრძელდება და მთელი შედეგი null გამოვა.

Groove ენაში არის ელვისის ოპერატორიც :D რომელიც ასე გამოიყურება ?:

String version = computer?.getSoundcard()?.getUSB()?.getVersion() ?: "UNKNOWN";

და მაგითი default მნიშვნელობა შეგვიძლია შემოვიტანოთ null-ის შემთხვევაში.

ბოლო რამდენიმე თვეა რაც Swift-ზე წერას ვცდილობ. ჯერჯერობით ენა არ არის სრულად ჩამოყალიბებული და ხშირად იცვლება. თუმცა ეს კონცეპტი ალბათ არსად წასვლას არ აპირებს. თავიდან ძალიან გამიჭირდა იმასთან შეგუება, რომ ენა გაიძულებს ცვლადის გამოცხადებისთანავე განსაზღვრო optional იქნება ის თუ არა. თუ კი, მაშინ ყველგან სადაც ამ ცვლადის გამოყენებას შეეცდებით, მისი null-ზე შემოწმება და მნიშვნელობის ე.წ. unwrap მოგიწევთ optional ტიპის ცვლადისგან. კოდი არ დაკომპილირდება სხვა შემთხვევაში.

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

CNC მანქანების დაპროგრამება

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

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

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

lcamtuf_robot1

ყალიბის გაკეთება რობოტის ნაწილებისთვის. ფოტო აღებულია lcamtuf-ის საიტიდან.

othermill

Othermill კომპანია otherfab-ისგან

jobs.ge საიტის REST კლიენტი ანდროიდზე

ალბათ გსმენიათ თბილისის GTUG-ის შესახებ – ეს არის თბილისის Google Technology User Group ქომუნითი, სადაც ჯერ ჯერობით მაინც დეველოპერები ჭარბობენ. ისინი ქმნიან და ერთმანეთს აცნობენ თავის პროდუქტებს, უყვებიან საკუთარ გამოცდილებას. ამიტომ GTUG-ის შეკრებებზე სხვადასხვა ტიპის სემინარებიც ტარდება ხოლმე. მაგალითად იყო მოხსენებები ანდროიდზე, RESTful ვებ სერვისებზე, რჩევებზე თუ როგორ შეიძლება შევქმნათ უკეთესი პროგრამული უზრუნველყოფა, Responsive web დიზაინზე, გუგლის App Engine-ზე, Cross-platform Mobile Development-ზე, Machine learning-ზე (გუგლის Prediction API), GWT-ზე, გუგლის რუკებზე, NoSQL მონაცემთა ბაზებზე, ალგორითმებზე…

როდესაც პირველი შეკრებები ტარდებოდა იქ ლაპარაკი იყო ანდროიდის აპლიკაციებზე. მეც დამაინტერესა და გადავწყვიტე ერთხელ მაინც მეცადა მობილურის აპლიკაციის დეველოპმენტი. საცდელად jobs.ge-ის კლიენტის წერა დავიწყე, რადგან ამ საიტს სახალხოდ ჰქონდა rss წყაროები. აპლიკაციის ნაწილი, რაც დავწერე, github-ზეა ხოლო მის შესახებ უფრო დეტალურად ამ დოკუმენტში წერია:

jobs.ge REST client application

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

GitHub-თან კავშირი პროქსის გავლით

არ მგონია რომ ეს პოსტი ბევრისთვის სასარგებლო გამოდგეს, მაგრამ მთელი დღე დამაკარგინა და ვერ მოვითმენ რომ არ დავწერო..

GitHub.com არის პროექტების ჰოსტინგის სერვისი ვებ ინტერფეისით, რომელიც Git version control სისტემას იყენებს. მოკლედ რომ ვთქვათ, თქვენ შეგიძლიათ იქ განათავსოთ პროექტის კოდები და ფაილები. ის ცვლილებების ისტორიასაც შეინახავს. შეიძლება სხვადასხვა განშტოებების და ვერსიების გამოყოფა და გუნდური მუშაობის დროს მოსახერხებელია კოდების ასე ცენტრალიზებულად არსებობა.

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

GitHub-ი რამდენიმე შესაძლებლობას გვაძლევს შესვლისთვის: ssh, https და git read-only
https-ით ssl-ის სერტიფიკატების პრობლემის გამო რატომღაც ვერ დავუკავშირდი, ამიტომ ვუკავშირდები ssh-ით ოღონდ https-ის პორტით – 443.

ჩემთან ასეთი ნაბიჯები შევასრულე:
~/.ssh საქაღალდეში შევქმენი კონფიგურაციის ფაილი config შემდეგი შიგთავსით:

Host github.com
User <მომხმარებელი>
Hostname ssh.github.com
ProxyCommand /c/connect.exe -H <პროქსის მისამართი>:<პროქსის პორტი> %h %p
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa
Port 443

ProxyCommand არის option, რომელიც OpenSSH-ში SOCKS პროტოკოლის შესაცვლელად ჩაემატა და საშუალებას იძლევა რომ სხვა პროგრამები გამოიყენოს ssh-მა პროქსისთან ურთიერთობისთვის. ერთ-ერთი ასეთი პროგრამაა connect.c

მე ვინდოუსიდან ვცდილობდი ატვირთვას ამიტომ გადმოვწერე კომპილირებული ფაილი აქედან: http://dl.dropbox.com/u/2177278/connect.exe (თავის საიტზე არ მუშაობდა ლინკი).

ასეთმა კონფიგურაციამ არ იმუშავა, მაგრამ connect.exe-ს აქვს ერთი კარგი პარამეტრი -d დებაგისთვის (მაგ: ProxyCommand /c/connect.exe -d -H <პროქსის მისამართი>:<პროქსის პორტი> %h %p) და მაგის მითითების შემდეგ უფრო დეტალურად წერს სად რა პრობლემა შეხვდა. ძალიან დიდი ალბათობით მანდვე იპოვით მიზეზს.

ჩემ შემთხვევაში პრობლემა პროქსისთან აუტენტიფიკაცია იყო. პროქსის პაროლის მითითებისთვის რამდენიმე მეთოდი არსებობს, მაგრამ environment variable-ებში ღია სახით გაწერა არ მომეწონა და არც იმუშავა ჩემთან. არც git-ის global http.proxy პარამეტრმა იმუშავა (აქაც ღია სახით იყო გაწერილი).

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

Cntlm სერვისია და ინსტალაციის შემდეგ ავტომატურად არის გაშვებული. შეგიძლიათ Control panel-ის Administrative Tools-ში შეხვიდეთ და ამოაგდოთ.
პროგრამა უსმენს კონფიგურაციაში გაწერილ მისამართს და პორტს, მაგალითად: 127.0.0.1:3128
ამიტომ ყველა იმ პროგრამის კონფიგურაციაში, რომელიც გინდათ რომ მისი გავლით პროქსის დააკავშიროთ, პროქსის მისამართის მაგივრად უნდა მიუთითოთ ეს მისამართი და პორტი.

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

მე Git Bash-დან ვუშვებდი ბრძანებებს და კავშირს ამით ვამოწმებდი ხოლმე:
$ ssh -v git@github.com

ამ პრობლემის გადასაჭრელად ძირითადად ეს პოსტები დამეხმარა:

Using Github Through Draconian Proxies
Git SSH problem – bad file number 

ცუდი ის იყო, რომ github-დან კლონი უპრობლემოდ გაკეთდა და ფაილები წამოიღო სერვერიდან, მაგრამ push-ის დროს არაფრით ამთავრებდა ოპერაციას (არც timeout შეცდომით) და მაგის გამო კავშირის პრობლემაზე არ მიფიქრია თავიდანვე. მერე გამახსენდა, რომ კლონი IDE-დან გავაკეთე.. და push არ ჰქონდა იქ..

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

ერთ-ერთი საიტის კოდში შემხვდა 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) ინახავს კეშირებულ ფაილებს მსგავსი სახით.