PHP4 מגיע לסוף החיים, אבל עדיין בשימוש של כ38% מהשרתים

שוחררה הגרסא האחרונה (ככל הנראה) בענף של PHP 4.4.
הגרסא הזו מסמנת את סוף התמיכה הרשמית בPHP4.
PHP5 כבר בחוץ במשך יותר משלוש שנים, אבל עד עכשיו האימוץ שלו היה די איטי, מסיבות של ביצה ודינוזאור:
מפתחי התוכנות לא רצו להפסיק לתמוך בPHP4 כדי לא לאבד משתמשים. חברות אירוח האתרים לא טרחו לשדרג כי כל התוכנות החשובות תמכו גם ככה בPHP4 והמשתמשים, מה איכפת להם?
בשנה שעברה נפתח אתר gophp5.org, ששם לעצמו למטרה לדחוף את האימוץ של PHP5.
הרעיון הוא שאם מסה מספיק גדולה של פרוייקטים תעבור לPHP5, ומסה מספיק גדולה של חברות אירוח תעבור לPHP5, לשאר חברות האירוח לא תהיה ברירה והן תאלצנה לשדרג או לאבד משתמשים, ואז לשאר הפרוייקטים לא תהיה סיבה להשאר בPHP4 והם יוכלו להתחיל סוף סוף לנצל את היכולות של PHP5.
בפברואר 2008 כבר היו מעל 100 פרוייקטי תוכנה שהתחייבו להפסיק לדאוג לתמיכה בPHP4 (זה לא אומר שהם ילכו וישברו את התמיכה בPHP4 בכוונה, פשוט שהם יפסיקו לדאוג שקוד חדש ירוץ בPHP4), ומעל 200 חברות איכסון שתומכות בPHP 5.2 כביררת מחדל וgophp5 טענו להצלחה והפסיקו לאסוף הרשמות.

אז למה לא לתמוך בPHP4? הנה דוגמא מאתמול:
JSON הוא דרך להעביר מבנה נתונים כלשהו לייצוג של מבנה הנתונים כאובייקט ג'אווה סקריפט, והוא אחת הדרכים הפשוטות והיעילות ביותר להעביר נתונים מקוד בצד השרת לדפדפן (שפשוט מפעיל על הטקסט שחוזר את המפסק (parser) של ג'אווה סקריפט כדי לקבל אובייקט מוכן לשימוש.
למרות שראשי התיבות של AJAX הן Asynchronous Javascript And XML, מעולם לא השתמשתי בAJAX כדי להעביר XML, למעשה אני מוצא את הרעיון מזעזע. הרבה יותר קל להעביר JSON, או אפילו קוד HTML ממש.
JSON משמש בהרבה מאוד פרוייקטים מבוססי AJAX, ומכיוון שאין בPHP4 תמיכה מובנית בJSON (אחרי הכל, PHP4 הוא בן שמונה, וJSON הוא די חדש בשכונה) צריך להשתמש בספריות חיצוניות שיודעות להעביר אובייקט PHP לפורמט JSON, אחת הספריות הנפוצות היא Services_JSON (למעשה אני משתמש בספריה הזו בFireStats).
הספריה כתובה בPHP, ולמרות שהיא עובדת נכון, היא לא ממש עובדת מהר, במיוחד כשממירים מבני נתונים גדולים (לא עצומים, משהו בסדר גודל של מערך עם 1000 אובייקטים טיפה מורכבים) לJSON.
ממש אתמול ניסיתי לשפר ביצועים של ישום PHP, אחרי חפירות גיליתי שאחד הדברים שמאטים מאוד את העסק היה המרה של תשובה לדפדפן לJSON בשימוש בServices_JSON, כשאני אומר איטי, אני מתכוון ל8 שניות.
שמונה שניות שהשרת טוחן את הCPU שלו כדי להכין תשובה ללקוח (במקרה הספציפי הזה, במקרים אחרים עם יותר נתונים זה כמובן יותר גרוע).
ברגע שראיתי את זה, לקח לי בדיוק שניה וחצי להיזכר שPHP5 תומך בJSON. בדיקה מהירה בphp.net הניבה את שתי הפונקציות הפשוטות json_encode וjson_decode. החלפתי את השימוש ב Services_JSON בקריאה לפונקציות של php5, ולא הייתי מופתע במיוחד לראות שהמרה של אותו מבנה נתונים לוקחת פתאום 40 מילישניות.
שיפור של פי 200, בזמן עבודה של כמה שניות (טוב, חוץ מלמצוא את הבעיה 🙂 )
השיפור נובע מכך שהתמיכה של PHP בJSON לא כתובה בPHP אלא בC, ולכן היא הרבה הרבה יותר יעילה.
השיפור הזה התאפשר רק כי הפרוייקט הספציפי הזה לא צריך לתמוך בPHP4.

מה אני אצטרך לעשות בFireStats, שעדיין תומך בPHP4 כדי להנות מהשיפור הזה? לבדוק אם אני על PHP5, ואם כן להשתמש בפונקציות האלו אחרת להשתמש בServices_JSON. לא כיף במיוחד.
ואם לא היתה לי סיפריה כמו Services_JSON (כי אין, או כי תנאי הרשיון לא מתאימים לי), הייתי נאלץ לכתוב אחת או פשוט לעבוד בצורה אחרת, פחות נוחה. גם לא כיף.

אז איך מתקדם האימוץ של PHP5?
החל מFireStats 1.3 ששחרתי לפני יותר משנה, FireStats מכילה רכיב ששולח (באישור המשתמש) מידע מערכת אנונימי. חלק מהמידע הוא גרסאות הPHP והMySQL.
הכוונה היא שאני אוכל להשתמש במידע הזה כדי להחליט בצורה יותר מושכלת במה אני צריך לתמוך.
המידע הצטבר לי בבסיס הנתונים, ונכון לכרגע יש לי מידע על כמעט 12000 התקנות. מה שאומר כמעט 12000 שרתים בעולים (אני מתעלם ממקרים של כמה התקנות על אותו שרת).
למי שתוהה, זה לא אומר שFireStats הותקנה 12000 פעמים, אלא ש12000 פעמים המתקינים הסכימו לשלוח מידע מערכת אנונימי.
מי שרוצה את המידע הגולמי מוזמן לקחת אותם מפה (יצוא MSQL, כ5 מגה דחוסים, 65 מגה פרושים).

אז ישבתי כמה שעות כדי להוציא מהנתונים הגולמיים שני גרפים נחמדים:
הראשון הוא אחוזי ההתקנות של PHP 4 מול PHP 5, במהלך השנה האחרונה.
למרות שאחוזי ההתקנה של PHP4 ירדו במהלך השנה האחרונה מ52% ל38%, עדיין מי אי אפשר להתעלם ממנו. נקווה שהוא ימות סופית בקרוב:
installed php versions
השני הוא אחוזי ההתקנות של הגרסאות המשמעותיות של MYSQL:

כיף לראות שMYSQL 5.0 שולט בשוק, אבל נראה שMYSQL 4.0 הזוועתי נתקע על 7% ולא רוצה למות.
בכל מקרה, MYSQL 4.0 הוא בהחלט מועמד לנטישה, וכבר היום יש לא מעט תכונות חשובות של FireStats שלא נתמכות בגרסא הזו.
installed mysql versions

משתמש תרם שיפורי ביצועים משמעותיים לIP2C

אם יש משהו שאני אוהב בפרוייקטי קוד פתוח, זה שאנשים מוכנים לפעמים לעבוד די קשה כדי לשפר אותם.
את IP2C, ספריה למציאת המדינה של כתובת IP שחררתי לפני כמעט שנתיים, וכתבתי גם פוסט שמספר על המימוש שלה פה.
IP2C ממומשת בPHP ובג'אווה. מה שמיוחד בה זה שהיא מסוגלת לחפש ישירות על הקובץ, מה שאומר שחיפוש בודד הוא מאוד מהיר כי לא צריך להעלות את כל הקובץ לזכרון.
הייתי לגמרי מרוצה מהביצועים של הספריה בPHP (כ1200 חיפושים בשניה במחשב האחרון שמדדתי), אבל הביצועים בג'אווה היו טובים יותר משמעותית – כ8000 חיפושים בשניה על אותו מחשב בעבודה ישירות על קובץ הנתונים.
ההבדל בביצועים בין PHP לג'אווה לא הטריד אותי, כי היה לי ברור שPHP תהיה יותר איטית מג'אווה, אבל הוא כן הטריד את תומס רומר שהתיישב על העסק לילה שלם ושיפור את הביצועים של גרסאת הPHP ב150%.
תומס כתב פוסט מעניין על השינויים שהוא עשה, ושלח לי את השינויים. שבמבט ראשון נראים טובים ואני אקלוט אותם לפרוייקט אחרי בדיקה מעמיקה יותר.

בנוסף דיברנו קצת בIRC, והוא יעבוד על תמיכה בבסיס הנתונים של software77 :
software77 מספקים בסיס נתונים של IP למדינה, שאמור להיות יותר איכותי מבסיס הנתונים שIP2C משתמשת בו כרגע (webhosting.info), אבל יש להם קצת בעיות בעקביות המידע.
התחלתי לעבוד על תמיכה בבסיס הנתונים שלהם לפני כמה חודשים טובים, אבל כשראיתי שזה נמשך יותר מדי הקפאתי את העסק (שעדיין נמצא בTODO שלי, קבור איפשהו 🙂 )
תומס ימשיך מאיפה שהפסקתי.

חולץ מאגר תגובות חבוי

לאחרונה יותר ויותר אנשים התלוננו שהבלוג אכל את התגובה שלהם.
למרבה הצער, לא ראיתי ברשימה של SK2 את התגובות הנ"ל, למעשה רשימת הספאם שלו היתה ריקה לגמרי.
לפני חמש דקות החלטתי לנסות לאפס את התוסף אחרי שקיבלתי עוד אימייל על ספאם שלאחריו הרשימה היתה ריקה.
לחצתי על כל הכפתורים האדומים הגדולים, ואחרי זה פתאום אני רואה את הרשימה של כל הספאם שנלכד לאחרונה.
אני אנסה לעבור עליו ולאשר את מה שרלוונטי.
אם הבלוג ממשיך לאכול לכם תגובות נא להודיע לי. (גם אימייל טוב, יש את האימייל בדף האודות).

iFuse

כשהיתי בסיאטל לפני כשמונה חודשים, קניתי לי אייפוד טאצ'.
המכשיר יפה, מהיר ומגניב אבל חסר בדבר אחד עיקרי:
חייבים להשתמש בiTunes כדי להעלות עליו קבצים.
iTunes היא אחת התוכנות המאוסות ביותר שאני מכיר, היא כבדה מאוד, הממשק שלה מעצבן ומתנשא (כן, ממשק מתנשא, יש דבר כזה), והיא מלאה באגים, וכדי לעצבן ממש – היא מחייבת חלונות או טייגר (Mac OS X).
השימוש העיקרי שלי בטאץ' הוא לשמיעת פודקסטים, ומטבע הדברים אני צריך לעדכן את העסק לפחות פעם בשבוע.
כשרק קיבלתי את המכשיר ביליתי די הרבה זמן בנסיונות להשתמש בVMWare או בVirtualBox (תוכנות ווירטואליזציה) כדי להריץ חלונות בתוך VM ועליו את הiTunes, אבל התמיכה של התוכנות האלו בUSB לא היתה מספיק טובה ונאלצתי להשתמש בעותק חלונות אמיתי כדי לעדכן את הקבצים, מה שגרם לכך שלפעמים לא עדכנתי אותם לתקופות ארוכות פשוט כי לא יצא לי להעלות את החלונות.
לפני כמה חודשים VMWare שיפרו מספיק את התמיכה שלהם בUSB והחל מגרסא שש בערך אני יכול לעדכן את האיפוד בלי לאתחל את המחשב, דבר ששיפר משמעותית את תדירות העדכונים שלי.
אבל עדיין, להריץ חלונות בתוך VMWare, ובתוכו להריץ את iTunes זה חתיכת מבצע כדי לעדכן קבצים.
אם פרצתם את הטאץ' שלכם, נוספה אפשרות נוספת:
להתחבר למכשיר דרך חיבור אלחוטי, לעגן את מערכת הקבצים עם sshfs (מערכת קבצים מבוססת fuse שמאפשרת לעגן דרך SSH).
העסק לא עבד מספיק טוב לטעמי, גם כי היו לי בעיות בחיבור האלחוטי וגם כי המכשיר אהב להכנס לסטנד-ביי תוך כדי עדכון, מה שגרם לחיבור להתנתק.
חוץ מזה זה פשוט לא עבד כמו שצריך ברמה של עדכוני הקבצים, למרות שאני מאמין שבחזית הזו המצב השתפר מאז.
התחלתי לחקור למה אין מימוש פתוח של פרוטוקול התקשורת שאפל מממשים על גבי חיבור הUSB, ונתקלתי בפרוייקט iFuse, ששם לעצמו מטרה לממש את הפרוטוקול ולספק כלי לעיגון מערכת הקבצים של אייפוד טאץ' ואייפון (שוב על בסיס fuse)
הפרוייקט היה תקוע במשך חודשים ארוכים בנסיונות למצוא דרך מסביב להצפנה בפרוטוקול (הפרוטוקול מוצפן בSSL, נבזי במיוחד מצד אפל), אבל לפני כמה שבועות היתה פריצת דרך ומאז יש התקדמות כמעט יומית.
נכון לכרגע קריאה של הקבצים, וקבלת רשימה של קבצים בספריה נתמכות יפה.
עדיין יש קשיים עם כתיבה של קבצים, אבל לדעתי זה עניין של כמה שבועות.
ברגע שהעסק יעבוד, תפתח הדרך לאינטגרציה אמיתית של הטאץ' עם מגוון התוכנות ללינוקס שמנהלות שירים על מכשירים נישאים.

קצרים

מייקרוספט תורמת 100,000 דולר לקרן התוכנה של אפאצ'י.
המהלך הופך את מייקרוסופט לספונסר פלטינום כמו גוגל ויאהו.

שאלתי כבר, האם מייקרוסופט משתנה?

——————–

דן קמינסקי – האקר, גילה חור אבטחה ברוב המימושים של שרתי הDNS היום שמאפשר התקפת הרעלת מטמון (Cache poisoning).
דן פנה במקביל למפתחות הDNSים הראשיות במטרה לארגן שחרור טלאי משותף במקביל, במטרה למזער את חלון ההזדמנות מרגע שהטלאי שוחרר לראשונה עד שהוא יהיה זמין לכל המימושים של שרתי הDNS.
אחרי חצי שנה של עבודה סודית הטלאי שוחרר אבל קמינסקי ביקש מקהילת ההאקרים לא לנסות לנחש מה החולשה, כי השוק צריך חודש כדי לבדוק ולהתקין את הטלאי.
החודש הזה נגמר באופן נוח בדיוק בתתאריך של כנס הכובעים השחורים, בו קמינסקי תכנן לספר לכולם על החולשה שהוא גילה, ולקטוף את הקרדיט (שמגיע לו).
ההתנהלות הזו עיצבנה לא מעט האקרים, כמה מהם הצליחו להבין את החולשה ושחררו קוד שמנצל אותה, מה שמעלה מאוד את הדחיפות של ההטלאה.

עוד על זה כאן.

רשת ג'יגהביט

הנתב הקודם – OfficeConnect של 3COM אף פעם לא היה טוב במיוחד.
החל מפינגים לא יציבים כאשר אני מתחבר דרך הנתב, וכלה במצב קטטוני עד כדי חוסר תקשורת בין שני המחשבים שמחוברים אליו שדורש אתחול של הנתב.
החלטתי להחליף את העסק, ובהתחלה חשבתי ללכת על נתב אולטימטיבי שיתן לי גם תקשורת אלחוטית מהירה, גם תקשורת מקומית מהירה וגם יכולת חיוג בכבלים ובADSL.
כשלא מצאתי משהו טוב מספיק שממש עשה לי את זה, החלטתי לשנות את האסטרגיה.
שינוי 1:
החלפת התשתית התקשורת שלי מכבלים לADSL.
על הדרך שדרגתי את חבילת התקשורת שלי מ1500/500 ל8000/800,
המחיר הוא 210 שקל לחודש עם התחייבות לשנה, כ90 שקלים יותר ממה ששילמתי קודם, וקו מהיר בהרבה – ויותר חשוב, יציב מאוד בניגוד לתשתית של הכבלים.
כחלק מהעסקה, ביקשתי מבזק לספק נתב. הם סיפקו נתב של ROTAL, לא פאר היצירה אבל מספיק טוב לצרכי התקשורת שלי מבחינת תקשורת אלחוטית ותקשורת אל האינטרנט.
הנתב יציב ולא נתקע כמו ה3COM המחורבן שהיה לי קודם, מה שפתר את הבעיה הזו, אבל לא נתן לי תקשורת יותר מהירה בין המחשבים.

שינוי 2:
כדי לפתור את הבעיה הזו, הזמנתי לי נתב ג'יגביט של Edimax, ושני כבלי רשת CAT6 שמתאימים לתקשורת הזו.
היום הם הגיעו, וחיש מהר התקנתי את הסוויץ', חיברתי אליו את המחשבים ובדקתי את המהירות:
קודם כל העתקת קובץ בין שני המחשבים. היה לי ברור שאני אתקע במגבלה של מהירות הכוננים הקשיחים שהיא נמוכה הרבה יותר מאשר ג'יגביט לשניה.
יצרתי קובץ של 1 ג'יגה, מלא לחלוטין באפסים עם
[code]
dd if=/dev/zero of=bigfile bs=1024 count=1000000
[/code]
שמתי אותו בספריה שחשופה בפרוטוקול HTTP, והורדתי עם WGET:
[code]
wget http://yadan.net/omry/bigfile

–2008-07-24 19:33:13– http://yadan.net/omry/bigfile
Resolving yadan.net… 10.0.0.2
Connecting to yadan.net|10.0.0.2|:80… connected.
HTTP request sent, awaiting response… 200 OK
Length: 1024000000 (977M) [text/plain]
Saving to: `bigfile'

100%[==============================================>] 1,024,000,000
39.6M/s in 25s

2008-07-24 19:33:38 (39.1 MB/s) – `bigfile' saved [1024000000/1024000000]
[/code]

כצפוי, לא מהירות שמרעידה את האדמה, אבל פי 3.5 יותר טוב ממה שנקבל ברשת 100 מגהביט בתאוריה (במעשי זה יותר מהיר פי 4-5).

לבדיקה הבאה רציתי מה המגבלה האמיתית של התשתית, בניטרול גורמים מציקים כמו מהירות הקריאה/הכתיבה של הכוננים.
חיפשתי קצת במאגר החבילות של דביאן, ומצאתי את iperf, תוכנית פשוטה לבדיקת ביצועי רשת.
iperf יודע לבדוק רוחב פס בתקשורת TCP או UDP, והוא עובד בשיטת שרת לקוח:
במחשב אחד מריצים אותו כשרת, במחשב השני כלקוח, ונותנים להם לבדוק את המהירות.
[code]

omry@home:~$ iperf -c 10.0.0.1
————————————————————
Client connecting to 10.0.0.1, TCP port 5001
TCP window size: 16.0 KByte (default)
————————————————————
[ 3] local 10.0.0.2 port 52685 connected with 10.0.0.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.0 sec 1.09 GBytes 940 Mbits/sec
[/code]

הרבה יותר מרשים.
אומנם לא הג'יגביט המובטח, אבל מספיק קרוב בשבילי, במיוחד לאור החומרה הזולה שקניתי לצורך העניין (סוויץ' ב150 שקל, וכבלי רשת של 15 מטר ו3 מטר ב70 ו30 שקל בהתאמה).

מכיוון שווידאו ב1080P הוא די גבולי על המחשב שיש לי בסלון (פנטיום D במהירות 3GHZ), החלטתי לנסות לבדוק איך הרשת סוחבת הזרמת וידאו ב1080P.
כדי לבדוק את זה, התחברתי מהמחשב בסלון אל המחשב בחדר (עם מעבד יותר חזק, Core 2 duo) עם ssh -X, מה שמאפשר לשלוח את הפלט של תוכניות גרפיות מהמחשב בחדר למחשב בסלון.
הפעלתי את mplayer במחשב בחדר על קובץ וידאו ב1080p (בטמן מתחיל), והפלט הוזרם למחשב בסלון.
הווידאו זז חלק לגמרי. אומנם לא שומעים כי הסאונד הולך למחשב הלא נכון, אבל זו בעיה אחרת ואני בטוח שאני אפתור אותה.
מה שלא ברור לי, זה למה בכלל זה עובד:
בהנחה של שכל פיקסל מיוצג ב4 בתים (ARGB), ושיש 25 פריימים לשניה (וכמובן כל בייט הוא 8 בתים), אז:
[code]
1920x1080x4x25x8=1,658,880,000
[/code]
1.6 ג'יגה ביט לשניה.
אפילו אם כל פיקסל הוא רק שלוש בתים (בלי A), ירדנו ל1.2 גיגהביט וזה עדיין לא מספיק.

אז איך בדיוק הסרט זז חלק על רשת של 1 גיגה ביט?

עדכון:
שמתי לב שmplayer מדווח על הפרמטרים המדוייקים של הסרט:
[code]
VIDEO: [avc1] 1920×800 24bpp 23.976 fps 0.0 kbps ( 0.0 kbyte/s)
1920x800x3x23.976×8=883,851,264
[/code]
שכבר מסתדר מצויין עם מה שראיתי ומדדתי.

EBuild

הפוסט הזה מיועד למתכנתי ג'אווה שעובדים עם Eclipse.

אז אתם מתחילים פרוייקט, מפתחים, משתמשים בכל מני JARים מפרוייקטים אחרים בתוך הסביבת עבודה, מוסיפים פרוייקטים אחרים לרשימת התלויות של הפרוייקט שלכם, והכל עובד בתוך Eclipse.
ואז אתם צריכים לשחרר JAR שירוץ מחוץ לסביבת הפיתוח שלכם.
פה יש כמה אפשרויות:
1. להשתמש באופצית הExport JAR של Eclipse.
2. ליצור build.xml לפרוייקט

לפרוייקטים פשוטים, האפשרות הראשונה תספיק, אבל היא בהחלט לא עושה נעים בבטן. כדי ליצור את הJAR חייבים את Eclipse, וזה לא מאוד מיקצועי.
האפשרוית השניה היא מה שכמעט כולם עושים:
בדרך כלל מעתיקים build.xml מהפרוייקט השכן, ומתחילים לעקם אותו עד שיתאים לפרוייקט הנוכחי.
על הדרך עוד פעם מוסיפים – הפעם לbuild.xml החדש – את המסלול לJARים שהפרוייקט צריך כדי להתקמפל, ואולי גם את המסלול לספריות הbin של פרוייקטים אחרים שהפרוייקט הזה צריך, או אולי פשוט קריאה לקובץ הbuild.xml של הפרוייקטים האלו, ואז שימוש בתוצרים שלו.
אחרי זמן שיקח בין חצי שעה לחצי יום, תלוי בבלאגן שיש לכם בפרוייקט, כנראה תהיה לכם מערכת build עובדת לפרוייקט, שעושה בדיוק מה שאתם רוצים.
תהיה לכם תחושה נעימה בבטן, כי תוכלו לבנות את הפרוייקט שלכם מחוץ לEclipse (וככה גם אחרים אם בניתם את העסק נכון).

אבל מה, הפרוייקט ממשיך לחיות:
עם הזמן, נוספים לו תלויות בJARים נוספים, תלויות בפרוייקטים נוספים בסביבת העבודה וכו', ואז מערכת הבילד המדוגמת שלכם נשברת, ואתם מרגישים פיכס, כי שוב אי אפשר לבנות את העסק מחוץ לEclipse עד שתתקנו את הבילד.
הפעם בדרך כלל העידכון של הבילד הוא יותר פשוט, כמה דברים קטנים והכל עובד שוב.

עכשיו, תכפילו את כל העסק הזה במספר הפרוייקטים שאתם מפתחים אקטיבית על פני כמה שנים, ומתחיל להיות פה משהו די מעצבן.
הרבה קבצי build.xml שעושים כמעט אבל לא בדיוק את אותו דבר, והרבה התעסקות איתם ברגע שמשהו משתנה.

המפתח העצלן כבר מזמן שאל את עצמו: "מה, אין דרך אחרת?"
הרי לרוב הפרוייקטים, Eclipse מכיל את כל המידע שצריך בשביל לבנות אותם.
מעבר לזה, המידע הזה תמיד נכון בהגדרה כי אחרת לא תוכלו לפתח כלום וזה יהיה הדבר הראשון שתתקנו ברגע שתעשו איזה שינוי.
Eclipse שומר את רוב המידע בתוך קבצי ה.claspath בתוך כל פרוייקט (וגם קצת בתוך קבצי ה.project). הקבצים האלו הם קבצי XML פשוטים למדי, שלא השתנו משמעותית מאז ימי Eclipse הראשונים.
אז הנה רעיון:
מה אם במקום לשכפל את המידע גם בEclipse וגם בbuild.xml, ניצור build.xml אחד שקורא את המידע על הפרוייקטים מEclipse, מבין מה סדר הבניה הנכון של הפרוייקטים, איזה JARים צריך לכל פרוייקט ובונה את העסק לפי זה?
אותו build.xml קסום ומופלא יעבוד כמעט לכל פרוייקט Java שפותח בתוך Eclipse בצורה שקופה, בלי שום התעסקות ותחזוקה של קובץ build.xml ספציפי לפרוייקט.
נשמע טוב מכדי להיות אמיתי?
ובכן, שחררתי מערכת כזו בדיוק, ולמערכת קוראים ebuild.
התחלתי לפתח אותה לפני שנים, והיא ליוותה אותי דרך ארבע מקומות עבודה עד עכשיו (למעשה דרך כל מקומות העבודה שהיו לי בתחום ההיטק).
המערכת משוחררת תחת רשיון FreeBSD (רשיון תעשו מה שבזין שלכם, לא מזיז לי) ודף הבית שלה הוא http://ebuild.firefang.net.
בגדול, כדי להשתמש בה, מה שצריך לעשות זה:

  • לקחת את הקוד שלה ולהכניס לפרוייקט נפרד בסביבת העבודה.
  • להעתיק את example-common.properties לcommon.properties (הוא מכיל הגדרות ספציפיות למחשב שלכם, למרות שכרגע אין מה להתעסק איתו ברוב המקרים).
  • ליצור קובץ build.properties בתוך הפרוייקט שאתם בונים (מאוד פשוט ומינימלי)
  • להריץ עם ant -Dproject=YOUR_PROJECT, כאשר YOUR_PROJECT הוא השם שם הפרוייקט שלכם בסביבת העבודה.
  • לקחת את התוצרים האיכותיים מספרית הbuild שנוצרה תחת הפרויקט.

התוצר יהיה JAR שניתן להריץ בעזרת java -jar file.jar, וכן זיפ שכולל את הJAR ואת הספריות הדרושות כדי להפיץ את התוכנית.

נכון לכרגע, המערכת די בסיסית ומסוגלת לבנות פרוייקטי Eclipse בתוך סביבת העבודה (Workspace) שלכם.
בהמשך אני מתכנן להוסיף אפשרות לתייג פרוייקטם ולבנות ישר מCVS/SVN בלי לגעת בקוד בסביבת העבודה (זה דרוש כדי לשחרר גרסאות בצורה מסודרת).
בנוסף, אני מתכנן להוסיף אפשרות לebuild-hook.xml אופציונלי שיהיה ספציפי לפרוייקט שיכיל כל מני דברים שבאמת ספציפיים לפרוייקט מסויים.

יש כמה דברים שכדאי לדעת:
באופן כללי, עדיף לעבוד עם JARים שנמצאים בתוך בworkspace כי אז החיים של מי שרוצה לבנות את העסק הם יותר קלים.

  • ebuild תומך בזה בצורה טובה, אבל לא ברור לי אם הוא יתמודד עם JARים חיצוניים.
  • ebuild לא תומך בכל מני הרחבות מוזרות של eclipse, שמסתמכות על מידע שלא מופיע בקבצי ה.classpath. למשל 'user libraries'. אם אתם רוצים שהוא יעבוד בשבילכם, תעבדו פשוט.

זהו.
אני אשמח אם הרבה אנשים ישתמשו בebuild, ידווחו על בעיות ואולי אפילו ישלחו תיקונים.

חור אבטחה בסקריפט דוגמא שPaypal מספקים

בדרך כלל אני נוטה לחשוב שבנקים ואתרים שהתעסקות עם כסף של אחרים היא הביזנס המרכזי שלהם לוקחים את האבטחה ברצינות.
על ההנחה הזו אני מבסס הרבה דברים, כמו למשל ההנחה שלי שקניה באינטרנט היא יותר בטוחה מקניה בטלפון, וזה שאיפשרתי העברת כספים מחשבון הבנק שלי בהוראה דרך האינטרנט.

באופן טבעי תפסתי גם את Paypal כאתר שלוקח אבטחה ברצינות.

החלטתי לצ'פר משתמשי פיירסטטס שתורמים ולתת כמה פיצ'רים למי שתורם בלבד.
מכיוון שאני מקבל את התרומות דרך Paypal חשבתי שזה יהיה נחמד לדאוג לאיזה תהליך אוטומטי שישלח לתורמים אימייל עם לינק להורדה של הפיצ'רים הנוספים.
פניתי לאתר של Paypal, שתומך במשהו שנקרא IPN : Instant payment notification.
בקצרה, האתר של Paypal קורא לURL שאתם מספקים לו ומעביר לו פרטים על הטרנזקציה (מי זה, כמה הוא נתן, וכו').
האתר שלכם מאמת את הפרטים מול Paypal, ומכניס אותם לבסיס הנתונים ועושה מה שאתם רוצים.

די פשוט, אבל חבל בזבוז אנרגיה שכל מי שרוצה להתממשק מול Paypal יצטרך לכתוב את זה מחדש, ולכן Paypal בחוכמתם מספקים Script generator שמייצר קוד שעושה את העבודה במספר שפות.

שמח וטוב לבב, לקחתי את סקריפט הPHP שהוא ייצר לי, והתחלתי לשחק איתו.
לא עבר יותר מדי זמן, ופתאום הבנתי שחוץ מזה שהקוד מכוער במיוחד הוא גם פרוץ לחלוטין ופגיע להתקפת SQL Injection.

[code lang="php"]
$struery = "insert into paypal_cart_info(txnid,itemnumber,itemname,os0,on0,os1,on1,quantity,invoice,custom)
values ('".$txn_id."','".$_POST[$itemnumber]."','".$_POST[$itemname]."','".$_POST[$on0]. "','".$_POST[$os0]."','".$_POST[$on1]."','".$_POST[$os1]."','".$_POST[$quantity]."','".$invoice."','".$custom."')";
$result = mysql_query($struery) or paypal_die("Cart – paypal_cart_info, Query failed:
" . mysql_error() . "
" . mysql_errno());
[/code]

SQL Injection היא התקפה שמאפשרת שינוי וקריאה של בסיס הנתונים בצורה שהמפתח לא תכנן, עוד פרטים בוויקיפדיה.

מה שזה אומר זה שכל מי שהשתמש בקוד לדוגמא שלהם בלי לשים לב לבעיה (אני מניח ש99% מהמשתמשים) יצר לעצמו בעיית אבטחה בשרת, בדיוק בנקודה הקריטית שנוגעת לכסף.

כתבתי להם הודעה בפורום המפתחים שלהם, נקווה שהם יקחו אותה ברצינות.

MySQL optimization

לפני כמה שבועות פרסמתי פוסט על אופטימיזציות.
הנה ההמשך.
דוגמא מעשית, שיפור ביצועים של שאילתות בFireStats (לגרסא 1.6)
מנגנון מקובל לשיפור ביצועים הוא מטמון (cache). הרעיון פשוט : בפעם הראשונה ששואלים אותנו משהו אנחנו מחשבים אותו וזוכרים את התוצאה. בפעם השניה אנחנו משתמשים בתוצאה שחישבנו קודם.
כשבודקים ביצועים, חשוב לוודא שהבדיקה נותנת תוצאות זהות בשתי הרצות שונות. במקרים רבים יש מנגנוני מטמון יגרמו לבדיקה לרוץ הרבה יותר מהר בפעם השניה, ובדרך כלל הם רק מפריעים בסיטואציות של בדיקת ביצועים כי אנחנו רוצים למדוד את השיפור שנובע מהקוד שלנו, ולא מזה שהCache הכיל את התוצאה שכבר חישבנו בהרצה הראשונה.
דוגמא לכך היא הCache של MySQL: ההרצה השניה של שאילת תמיד תהיה הרבה יותר מהירה מההרצה הראשונה, כי MySQL זוכר את התוצאות מהפעם הראשונה ואם שום דבר בנתונים לא השתנה הוא פשוט מחזיר את אותה תוצאה.

כדי לבטל את אותו Cache, אפשר להשתמש בפקודת הMySQL:
[code lang="sql"]
SET GLOBAL query_cache_size = 0;
[/code]

FireStats תומך בגרסאות MySQL ממשפחת 4.0, 4.1 ו5.0, כאשר ההבדל בין 4.0 ל4.1 כל כך משמעותי שיש שאילתות בFireStats שכתובות אחרת לכל אחת מהגרסאות.
נסיון לשיפור ביצועים, במיוחד כזה שמבוסס על שינויים בסכמת הנתונים יצריך שינויים בקוד שמטפל ב4.0 ובקוד של 4.1, לכן כדאי לבדוק שהכל עובד על השרת הרלוונטי. בנוסף, מכיוון שרוב (60%) משתמשי FireStats עובדים עם MySQL 5.X, כדי גם לבדוק את השיפור על השרת הזה.
כדי לבצע את זה, הרמתי שלושה שרתי MySQL, נציג אחד מכל משפחת גרסאות, והכנסתי לכל שרת חבילת נתונים די כבדה (וזהה בין השרתים) שמבוססים על סטטיסטיקות מהשרת שלי.
ובכל אחד מהשרתים ביטלתי את מטמון השאילתות (query_cache_size).
יצרתי טבלא בגנומטיק (כמו אקסל), שנראית ככה (תת טבלא כזו לכל בדיקה שאני רוצה)
fs-opt.jpg

נניח שהפונקציה שאני רוצה לשפר היא:
[code lang="php"]
function foo()
{
$sql = "SELECT * from …";
return query($sql);
}
[/code]

קודם כל הכפלתי את הקוד והוספתי הדפסה של שאילת הSQL שנשלחת לשרת:
[code lang="php"]
function foo()
{
if (true) // old code
{
$sql = "SELECT * from …";
echo $sql; return;
return query($sql);
}else{
$sql = "SELECT * from …";
echo $sql; return;
return query($sql);
}
}
[/code]
מכאן זו היתה שיטת העבודה:
1. הרצה של הקוד במצב הישן.
2. העתקה של השאילתה שהודפסה, והרצה שלה ישירות על כל אחד משלושת בסיסי הנהנתונים. ושמירה של הזמן שנדרש בטבלא מסודרת.
3. מעבר למצב חדש (פשוט לשנות את הfalse לtrue בראש הפונקציה), אופטימיזציה של השאילתה וחזרה על הבדיקה מול כל אחד מהשרתים, ושוב שמירה של התוצאות בטבלא, הפעם בעמודה של תוצאות אחרי אופטימיזציה.

העבודה השיטתית עזרה לי לא לשכוח דברים תוך כדי, ולוודא שאכן שיפרתי את התוצאות לפני שאני רץ להכניס את הקוד למערכת.
בנוסף, מכיוון שגם הקוד החדש וגם הישן היו לי מול העיניים, יכלתי לוודא שהם עושים בדיוק את אותו דבר.
חשוב לשים לב שלא ערבבתי אופטימיזציה של קוד הPHP עם שאילות הMYSQL. זה מבלבל מספיק גם ככה.

אז מה בעצם עשיתי?
פיירסטטס תומך בכמה אתרים בו זמנית, ולכן יש בו מזהה אתר. בהתחלה שמרתי את מזהה האתר בטבלאת הכניסות, בשלב מסויים הבנתי שאני צריך מזהה אתר גם בטבלאת הכתובות (URLים), כי הרי כל URL שיך לכל היותר לאתר אחד.
אז הוספתי את העמודה, אבל שכחתי למחוק את העמודה מטבלאת הכניסות.
מצב כזה של כפילות נתונים הוא לא מומלץ לפי הגישה המקובלת בתכנון בסיסי נתונים, כי הוא מאפשר מצבים של חוסר עקביות במידע.
באחת הגרסאות האחרונות של פיירסטטס תיקנתי את המעוות: הסרתי את העמודה מטבלאת הכניסות ותיקנתי את כל הקוד שהושפע מזה לבצע join עם טבלאת הכתובות כשהוא צריך גישה לאתר.
על פניו הדבר הנכון לעשות: אבל זה יצר בעיה חדשה:
כמעט כל השאילתות דרשו עכשיו join נוסף. עבור משתמשים עם בסיסי נתונים קטנים (נניח עד חצי מליון כניסות בטבלאת הכניסות ועד 50 אלף כתובות בטבלאת העסק עבד סביר, אבל עבור משתמשים עם בסיסי נתונים גדולים יותר העומס על השרת התחיל לגדול בצורה חדה ככל שנפח הנתונים עלה.
הפתרון שלי הוא לחזור בי מהדבר "הנכון" שעשתי קודם, כלומר להחזיר את עמודת הsite_id לטבלאת הכניסות, שתשמש לצרכי פילטור בזמן השאילות כדי להמנע מהjoin עם טבלאת הכתובות.

שיפור נוסף נבע משינוי מבני של השאילות ככה שיעבדו בצורה יותר יעילה.
כדי לעשות את זה, חשוב להבין מה עושה בסיס הנתונים כדי לחשב את השאילתה.
לשם כך אפשר להשתמש בפקודה explain, שמחזירה תיאור של האסטרטגיה שבה בסיס הנתונים יחשב את התוצאות.
explain מחזיר תוצאות די קריפטיות, לא משהו שתבינו בלי לקרוא את התיעוד.
בכך מקרה, חשוב לשים לב לעובדות הבאות:
בכל שלב, MySQL משתמש באינדקס אחד בלבד לכל היותר (אם יש אינדקס מתאים). ככל שהאינדקס יהיה יותר ספציפי ככה הביצועים יכולים להיות טובים יותר.
(יתכן שגרסאות עתידיות של MySQL ידעו להשתמש בכמה אינדקסים בשלב, אבל לדעתי זה עדיין לא פה).

ככה נראית הטבלא שלי אחרי שמילאתי אותה:

Query name MySQL Version 4.0.17 4.1 5.0.51
Num page views for all hits in site=1 Original query time sec 1.20 0.93 0.84
Optimized query time sec 0.10 0.06 0.06
Improvement % 91.67% 93.55% 92.86%
Num page views for all hits in all sites Original query time sec 1.17 0.91 0.84
Optimized query time sec 0.11 0.08 0.08
Improvement % 90.60% 91.21% 90.48%
Num page views for for last last 14 days for all sites Original query time sec 0.19 0.16 0.16
Optimized query time sec 0.12 0.09 0.11
Improvement % 36.84% 43.75% 31.25%
Num all unique visitors for site_id = 1 Original query time sec 2.93 1.24 1.13
Optimized query time sec 1.75 0.68 0.72
Improvement % 40.27% 45.16% 36.28%
Num all unique visitors for all sites Original query time sec 2.09 1.24 1.15
Optimized query time sec 1.26 0.47 0.47
Improvement % 39.71% 62.10% 59.13%
Num unique visitors for last 14 days for all sites Original query time sec 0.32 0.18 0.18
Optimized query time sec 0.28 0.16 0.15
Improvement % 12.50% 11.11% 16.67%
Recent referrers for all sites Original query time sec 2.54 3.01 3.01
Optimized query time sec 2.30 1.54 1.78
Improvement % 9.45% 48.84% 40.86%
Recent referrers for site_id = 1 Original query time sec 2.48 2.71 2.76
Optimized query time sec 2.65 1.65 1.90
Improvement % −6.85% 39.11% 31.16%
Search terms for site_id = 1 Original query time sec 1.67 2.14 2.75
Optimized query time sec 1.67 0.84 0.91
Improvement % Not optimized 60.75% 66.91%
Popular pages (all) Original query time sec 1.88 4.67 4.80
Optimized query time sec 1.73 1.96
Improvement % Not optimized 62.96% 59.17%
Popular pages (site_id = 1) Original query time sec 1.93 4.07 4.09
Optimized query time sec 1.88 2.11
Improvement % Not optimized 53.81% 48.41%
get questagents (180 days, site_id = 1) Original query time sec 6.89 2.90 2.35
Optimized query time sec 5.07 1.00 1.10
Improvement % 26.42% 65.52% 53.19%
Countries (20 countries, 180 days, site_id = 1) Original query time sec 1.95 1.08 1.08
Optimized query time sec 0.30 0.20 0.23
Improvement % 84.62% 81.48% 78.70%
Hits table (100) Original query time sec 0.30 0.61 0.42
Optimized query time sec
Improvement % Not optimized Not optimized Not optimized