Tampilkan postingan dengan label delphi. Tampilkan semua postingan
Tampilkan postingan dengan label delphi. Tampilkan semua postingan

Selasa, 14 November 2023

Creating A Thread Safe Database Connection Pool for Delphi and Free Pascal based Lazarus

One day I got a software project to create an application that need to be accessed from many computer with one data server. I decided to create a three-tier application, a client application for the client computer and a middle application that connect directly to database server that is a Firebird Database Server for this project. After a few weeks the project is finished and I realized that the response time of every client request is very slow caused by database connection time. Because every time client request arrived the middle application create new thread to handle the request and create new database connection and doing database query and then send the respond data back to the requester client and finally close database connection and free database connection, transaction and queries objects before the thread is destroyed.

Then I begin researching to reduce the database connection time in the request handler thread. I become pesimistic after I read in delphi manual book that every thread must create and use its own database connection. But I'm still not giving up because other programming like PHP and MySQL can do that. Then I try to isolate every parts of the database connection and database query in part by part, and finally I'm able to reuse database connection, transcation and query object with many threads by isolating creation and desctruction of database connection object and transaction object and also commit and rollback process, everything else is no problem with multithread.

  1. procedure TSimpleQuery.Commit;
  2. begin
  3.   if ((FQuery.Connection = nil) or not FQuery.Connection.Connected or
  4.     FQuery.Connection.AutoCommit) then exit;
  5.   EnterCriticalSection(FConnectionManager.GetCriticalSection^);
  6.   try
  7.     try
  8.       FQuery.Connection.Commit;
  9.     except
  10.       LogExceptionMessage;
  11.       raise;
  12.     end;
  13.   finally
  14.     LeaveCriticalSection(FConnectionManager.GetCriticalSection^);
  15.   end;
  16.   ConnectionManager.SetAllowFailover(FQuery, True);
  17. end;

  18. procedure TSimpleQuery.Rollback;
  19. begin
  20.   if ((FQuery.Connection = nil) or not FQuery.Connection.Connected or
  21.     FQuery.Connection.AutoCommit) then exit;
  22.   EnterCriticalSection(FConnectionManager.GetCriticalSection^);
  23.   try
  24.     try
  25.       FQuery.Connection.Rollback;
  26.     except
  27.       LogExceptionMessage;
  28.       raise;
  29.     end;
  30.   finally
  31.     LeaveCriticalSection(FConnectionManager.GetCriticalSection^);
  32.   end;
  33.   ConnectionManager.SetAllowFailover(FQuery, True);
  34. end;

After succeeded creating reusable database connection object accross multiple threads then I create wrapper component for the database connection, transaction dan query object like this:
  1.   ISimpleQuery = interface
  2.     ['{1C1949E4-C472-45BA-8638-5A14545592F8}']
  3.     function GetConnection: TZConnection;
  4.     function GetConnectionManager: IConnectionManager;
  5.     function GetParentQuery: ISimpleQuery;
  6.     function GetQueryObject: TZReadOnlyQuery;
  7.     function GetPrepared: Boolean;
  8.     function GetRecordCount: Integer;
  9.     function GetRowsAffected: Integer;
  10.     function GetActive: Boolean;
  11.     procedure SetActive(AValue: Boolean);
  12.     function GetName: String;
  13.     procedure SetName(const AValue: String);
  14.     function GetFieldDefs: TFieldDefs;
  15.     function GetFields: TFields;
  16.     function GetSQL: TStrings;
  17.     function GetBOF: Boolean;
  18.     function GetEOF: Boolean;
  19.     function GetParams: TParams;
  20.     function GetParamCheck: Boolean;
  21.     procedure SetParamCheck(Value: Boolean);
  22.     function GetConnected: Boolean;
  23.     function GetAutoCommit: Boolean;
  24.     procedure SetAutoCommit(const Value: Boolean);
  25.     function GetRecordAccess: TRecordAccess;
  26.     function GetPost: ITransportVar;
  27.     function GetWhere: ITransportVar;

  28.     procedure Open;
  29.     procedure OpenQuery(const ASQL: String);
  30.     procedure ExecSQL;
  31.     procedure ExecuteQuery(const ASQL: String);
  32.     procedure Close;
  33.     procedure First;
  34.     procedure Next;
  35.     procedure Prior;
  36.     procedure Last;
  37.     procedure Commit;
  38.     procedure Rollback;
  39.     procedure Prepare;
  40.     procedure Unprepare;
  41.     function CreateQuery: ISimpleQuery;
  42.     function FieldByName(const AFieldName: String): TField;
  43.     function FindField(const AFieldName: String): TField;
  44.     function ParamByName(const AParamName: String): TParam;
  45.     procedure Select(const SelectedCols: String);
  46.     procedure Group(const GroupBy: String);
  47.     procedure Order(const OrderBy: String);
  48.     procedure Get(const Table: String);
  49.     procedure Insert(const Table: String);
  50.     procedure Update(const Table: String);
  51.     procedure Delete(const Table: String);
  52.     function GetSQLWhere(AWhere: ITransportVar): String;
  53.     procedure AllowPost(const Columns: String);

  54.     property ConnectionManager: IConnectionManager read GetConnectionManager;
  55.     property ParentQuery: ISimpleQuery read GetParentQuery;
  56.     property Active: Boolean read GetActive write SetActive;
  57.     property Connection: TZConnection read GetConnection;
  58.     property QueryObject: TZReadOnlyQuery read GetQueryObject;
  59.     property FieldDefs: TFieldDefs read GetFieldDefs;
  60.     property Fields: TFields read GetFields;
  61.     property QueryFields[const AFieldName: String]: TField read FieldByName; default;
  62.     property SQL: TStrings read GetSQL;
  63.     property BOF: Boolean read GetBOF;
  64.     property EOF: Boolean read GetEOF;
  65.     property RowsAffected: Integer read GetRowsAffected;
  66.     property RecordCount: Integer read GetRecordCount;
  67.     property Prepared: Boolean read GetPrepared;
  68.     property Params: TParams read GetParams;
  69.     property ParamCheck: Boolean read GetParamCheck write SetParamCheck;
  70.     property Connected: Boolean read GetConnected;
  71.     property AutoCommit: Boolean read GetAutoCommit write SetAutoCommit;
  72.     property Name: String read GetName write SetName;
  73.     property RecordAccess: TRecordAccess read GetRecordAccess;
  74.     property Post: ITransportVar read GetPost;
  75.     property Where: ITransportVar read GetWhere;
  76.   end;

I'm using reference counter in the Interface type to make coding faster by creating query interface object and after the interface object is unused then it's automatically freed and the database connection object, transaction object, the wrapped query object is deallocated and waiting for next use.

I develop two version one with ZeosDBO component and another one with SQLDb component  and both working and very stable.

After I upgraded my application with this database connection pool the client request handling is faster than before.

I'm sorry about my bad english this is the first time i write article in english because maybe alot programmer outside of my country require this knowledge.

Rabu, 30 Agustus 2023

Alasan menggunakan Lazarus dan Delphi di zaman sekarang

Zaman sekarang development tools yang bagus dan sangat maju dan sedang berkembang pesat banyak seperti Java, Java Script, PHP, C#, Go, Dart, Kotlin, C++, Python dan masih banyak lainnya. Diantara banyaknya development tools yang ada dalam keseharian saya masih sering menggunakan aplikasi Lazarus dan Delphi untuk mendevelop aplikasi. Lazarus sendiri merupakan IDE (Integrated Development and Environment) yang memanfaatkan compiler Free Pascal.

Beberapa alasan saya untuk menggunakan Lazarus dan Delphi antara lain sebagai berikut:

  1. Karena sudah sangat nyaman karena terbiasa jadi begitu ada kebutuhan untuk mendevelop aplikasi pakenya ya itu-itu saja.
  2. Menggunakan resource processor dan memori yang kecil sehingga orang-orang seperti saya yang tidak memiliki hardware komputer highend terbaru dengan processor terbaru dan ram yang besar tetap bisa menjalankan aplikasi ini. Zaman saya kuliah dulu cuma menggunakan komputer AMD Sempron 2,1 GHz dengan RAM 1 GB saja masih bisa menjalankan program Delphi 2007 dengan lancar jaya.
  3. Menghasilkan file executable native code dengan optimalisasi yang bagus sehingga bisa berjalan dengan cepat dengan hardware seadanya. Khusus untuk Lazarus dan Delphi yang baru sudah mendukung prosesor 64-bit dengan optimal, sedangkan Delphi 2010 ke bawah masih terbatas pada processor 32-bit.
  4. File executable yang dihasilkan tidak bergantung pada file library yang lain sehingga cukup mendistribusikan satu file executable saja, kecuali memang menggunakan library khusus misal untuk modulasi program atau driver untuk koneksi ke database sehingga memerlukan file library lain tapi terbatas pada library yang dibutuhkan saja. Karena cukup mendistribusikan satu file executable saja sehingga mempermudah deployment dan meminimalkan penggunaan space hard disk maupun ram.
  5. Mungkin karena masih turunan Delphi 1.0 yang dulunya didesain untuk Windows 3.1 yang masih mendukung komputer dengan RAM sekecil 4 MB sehingga standar library bawaan Lazarus dan Delphi didesain agar sangat efisien dalam menggunakan RAM.
  6. Sangat mudah untuk membuat daemon atau windows service sehingga cocok untuk membuat sebuah aplikasi server kecil-kecilan.
  7. Bisa mengakses hardware yang bisa digunakan misalnya untuk membuat SMS Gateway dengan modem GSM.
  8. Khusus untuk Free Pascal, Lazarus dan Delphi terbaru bisa untuk mendevelop multiplatform misal bisa jalan di Windows dan Linux sehingga sangat membantu sekali misalnya ketika harus mendevelop aplikasi server yang dijalankan di VPS Linux yang lebih murah. 

Bagaimanapun juga sebagai development tool tetaplah hanya sebuah tool saja yang membutuhkan programmer untuk memanfaatkannya sehingga menghasilkan produk yang bagus dan bermanfaat. Saya percaya sebuah tool yang bisa memberikan manfaat bagi penggunanya tidak akan pernah punah dan menghilang.

Sekian artikel ini semoga bermanfaat.

Senin, 29 Juli 2013

Mengakses Daftar Method dari class dengan String pada Lazarus

Suatu hari aku memiliki pekerjaan yang mengharuskanku untuk membuat sebuah RPC yang sesederhana mungkin dan fungsional dengan lazarus. Menurutku sih itu mudah banget secara dah banyak komponen socket TCP/IP dan contoh program server HTTP jadi aku berencana memakai http protokol sebagai sarana komunikasi server dengan kliennya.

Tapi entah kenapa kok saya tiba-tiba malas banget untuk meregistrasi setiap prosedur yang dipublish oleh modul web servicenya, sehingga aku membuat sebuah fungsi untuk meregistrasi semua fungsi didalam class modul servis yang dipublish secara otomatis dari informasi class itu.

Pertama-tama aku mencoba mendeklarasikan tipe untuk mengakses struktur tabel method dari suatu class di lazarus, sebagai berikut:

type
tmethodnamerec = packed record
name : pshortstring;
addr : pointer;
end;

tmethodnametable = packed record
count : dword;
entries : packed array[0..0] of tmethodnamerec;
end;

pmethodnametable = ^tmethodnametable;

// type untuk menjalankan method lengkap dengan parameternya
TTestMethod = procedure(const AData: String) of object;

Setelah itu semua method yang akan diakses dideklarasikan sebagai published, sebagai berikut:

type
TMyApplication = class(TCustomApplication)
published
procedure TestReadMethod;
procedure Test1(const AData: String);
.
.

Kemudian berikut method yang digunakan untuk membaca daftar method terpublish dan contoh menjalankan sebuah method dengan string:

procedure TMyApplication.TestReadMethod;
var
methodtable : pmethodnametable;
i : dword;
ovmt : PVmt;
m : TTestMethod;
begin
ovmt:=PVmt(self.ClassType);
while assigned(ovmt) do
begin
methodtable:=pmethodnametable(ovmt^.vMethodTable);
if assigned(methodtable) then
begin
for i:=0 to methodtable^.count-1 do
begin
WriteLn('Found method: ',methodtable^.entries[i].name^);

// jika menemukan fungsi Test1 jalankan dengan parameter '
Halo'
if (SameText(methodtable^.entries[i].name^,'Test1')) then
begin
TMethod(m).Code := methodtable^.entries[i].addr;
TMethod(m).Data := Self;
m('Halo');
end;
end;
end;
ovmt := ovmt^.vParent;
end;
end;

Source code dari contoh fungsi yang dijalankan:

procedure TMyApplication.Test1(const AData: String);
begin
WriteLn('Test1 dijalankan dgn parameter ',AData);
end;

Dari contoh program di atas output program yang didapatkan adalah sebagai berikut:

Dari output di atas bisa disimpulkan bahwa source code yang saya buat telah berhasil membaca daftar method dari class dan menjalankan salah satu fungsi dengan String.

Sekian contoh pengalaman saya dalam membuat fungsi untuk membaca method secara otomatis dari class di lazarus, semoga bermanfaat.

Selasa, 16 Juli 2013

Mencoba membuat sebuah aplikasi web dengan server buatan sendiri dengan Lazarus

Singkat cerita pada suatu waktu saya memiliki waktu luang yang sangat melimpah, semacam bergelimang waktu luang gitu deh. Karena aku itu hiperaktif jadi aku manfaatkan untuk mencoba membuat sebuah aplikasi web dengan web server yang aku buat sendiri make lazarus.

Dari situ aku mulai merancang modularitasnya agar masing2 modul aplikasi bisa lebih fokus pada urusannya sendiri namun tetap bisa saling komunikatif agar hubungannya tidak retak.. halah..

Setelah beberapa hari didevel maka akhirnya software coba-coba saya mulai menampakkan hasil, berikut screen capturenya:

Tampilan control panel utamanya:

Tampilan opsi servernya:

Tampilan aplikasi web home screen sblm login:

Tampilan home screen aplikasi saat login:

Tampilan salah satu menu program:

Tampilan halaman browsing datanya:

Tampilan form edit datanya:

Tampilan programnya lumayan bagus dan enak utk membuat skinnya karena bisa make css ala web. Utk komunikasi dengan javascriptnya aku bikin sendiri rutinnya dengan transportnya masih make json.

Tetapi karena setelah diujicoba terasa terlalu rumit dalam developmen aplikasi maka project ini terpaksa dirombak total dan sampai saat ini aku belum punya waktu untuk melanjutkannya.

Minggu, 26 Juli 2009

Lazarus, sebuah IDE Open Source yang menyerupai Delphi

Lazarus adalah sebuah visual IDE (Integrated Development Environment) yang cross platform dan menyerupai Delphi untuk para developer Pascal dan Object Pascal. Lazarus dibangun untuk dan didukung oleh Free Pascal Compiler. Mulai Maret 2008 Lazarus telah tersedia untuk beberapa distro Linux, Free BSD, Microsoft Windows dan Mac OS X.

Lazarus adalah software gratis seperti Free Pascal. Didistribusikan dengan GNU Lesser General Public License yang diubah. Perubahan memungkinkan lazarus untuk digunakan pada software prorietary.

Free Pascal adalah sebuah kompiler yang berjalan pada banyak sistem operasi. Didesain untuk mengkompilasi source code dalam bahasa Object Pascal sebuah penambahan dari bahasa pemrograman Pascal.

Berbeda dengan Java yang dirancang supaya write once, run anywhere, Lazarus dan Free Pascal dirancang supaya write once, compile everywhere. Karena kompiler yang sama tersedia untuk semua sistem operasi di atas sehingga tidak dibutuhkan coding ulang untuk menghasilkan produk untuk platform-platform yang berbeda, kecuali jika menggunakan fitur yang tergantung pada sistem operasi tertentu. Cross-compiling juga didukung.

Lazarus mulai versi 0.9.26.2 sudah sangat stabil dan bisa dibandingkan dengan Delphi 7.

Kelebihan Lazarus jika dibandingkan dengan Delphi adalah sebagai berikut:

  1. Open Source dan Gratis
  2. Multiplatform, mendukung Windows, Linux, Mac OS dan Pocket PC
  3. Bisa menghasilkan code 64-bit
  4. Dikembangkan oleh komunitas open source sehingga berkembang dengan sangat pesat

Kekurangan Lazarus jika dibandingkan dengan Delphi:

  1. Kurang stabil (versi 0.9.26 ke atas sudah sangat stabil).
  2. Untuk memasang komponen harus mengkompilasi ulang IDE.
  3. Dukungan komponen fihak ke-tiga yang dibuat perusahaan komersial yang berkualitas dan layak digunakan untuk produksi masih kurang, misal: belum tersedianya komponen2 SUIPack, TMS Advanced String Grid, Fast Report, dsb.
  4. Dukungan untuk sistem operasi Windows masih kalah, misalnya: belum bisa mengimpor COM dan ActiveX.
  5. Tidak bisa meletakkan kelas di dalam library.

Lazarus ke depan sangat berpotensi untuk digunakan dalam pembuatan software aplikasi yang cross-platform dan berkualitas. Bahkan saat ini sudah mulai banyak software aplikasi yang dibangun dengan Lazarus, untuk lebih jelasnya silahkan buka http://en.wikipedia.org/wiki/Lazarus_(software) atau investigasi langsung ke situs resmi Lazarus http://lazarus.freepascal.org.

Creating Linux Daemon or Windows Service with Lazarus

Daemon Application in Linux or Service Application in Windows is an application that running in the background, usually automatically starte...