|
The first thing we noticed when we started testing b-Commerce over the Internet was "It works! Cool!" The second thing we noticed was that it was too slow to be usable. The reason was clear enough in retrospect. We developed b-Commerce using GUIBuilder®, which was written with an implicit assumption that the resulting program would run under Visual PRO/5®. Client/server issues weren't a factor in the design of GUIBuilder because Visual PRO/5 isn't a client/server platform. It turns out that when the client side (screen, keyboard and mouse) is separated from the server side (the business rules and database) by thousands of miles of copper and fiber, the application needs to work differently. When you're writing a distributed application, you need to take bandwidth and latency issues into account. Bandwidth is a measure of how much data you can send in a given time. It's typically 28.8 kilobits per second (kbps) up to 53 kbps for an analog modem. Other technologies such as integrated services digital networks (ISDN), digital subscriber lines (DSL) and cable modems offer substantially higher bandwidth. The smaller the bandwidth, the more important it is to reduce the volume of data being transferred.
Bandwidth
Latency
[c:\basis]ping www.basis.com Pinging www.basis.com [207.66.35.68] with 32 bytes of data: Reply from 207.66.35.68: bytes=32 time=120ms TTL=240 Reply from 207.66.35.68: bytes=32 time=100ms TTL=240 Reply from 207.66.35.68: bytes=32 time=91ms TTL=240 Reply from 207.66.35.68: bytes=32 time=110ms TTL=240 [c:\basis] In other words, each time you exchange any information with the BASIS Web server, even a single byte, the round trip will take, on average, a minimum of 100 milliseconds (ms), or 1/10th of a second. This contrasts with typical ping times of 10 ms for an ISDN connection and 0.3 ms for a local Ethernet connection. A widely distributed application should be designed to minimize the number of times the client and server need to talk to each other. With this principle in mind, we analyzed what the b-Commerce application was doing. When we considered code like the following, we realized that each PRINT statement requires a network access, so this block of code was taking 500 ms to execute:
print (gb__sysgui)'context'(gb__win.login) print (gb__sysgui)'enable'(0),'show'(0),'focus'(0),'raise' print (gb__sysgui)'focus'(ctl_id) print (gb__sysgui)'context'(gb__win.main),'hide'(0) print (gb__sysgui)'context'(gb__win.login)' We rewrote this code by combining multiple PRINT statements into a single command:
print (gb__sysgui) : 'context'(gb__win.login)' : 'enable'(0),'show'(0),'focus'(0),'raise','focus'(ctl_id), : 'context'(gb__win.main),'hide'(0), : 'context'(gb__win.login) This is functionally identical to the first version, but assuming a network latency of 100 ms, it will execute in 0.1 seconds as opposed to 0.5 seconds. We made this kind of change about 70 times in the b-Commerce program. Then we considered the number of network accesses we were doing each time we update a screen. Applications developed with GUIBuilder typically use code like this to update the screen:
rem ' Initialize data structures for the login screen win_id_login$ = fngb__win_id$(gb__win.login) dim win_login$:fngb__template$(win_id_login$) rem ' Clear a couple of fields on the screen win_login$ = fngb__get_screen$(win_id_login$,win_login$) win_login.user_id$ = "" win_login.password$ = "" win_login$ = fngb__put_screen$(win_id_login$,win_login$) This code does the following:
This strategy works well in Visual PRO/5, and it works well in BBj when the application is running on a single machine. But in a distributed application, we need better control over the number of network accesses and the volume of data being transmitted. So we added two new functions that allow us to limit the update to selected fields. With these new functions, we can update two fields on the screen with code like this:
rem ' Clear a couple of fields on the screen win_login.user_id$ = "" win_login.password$ = "" win_login$ = fngb__put_fields$(win_id_login$,win_login$,"user_id,password") This code is more efficient because we've eliminated the initial screen read and we've limited the screen update to just the two fields being changed. This optimization improved performance significantly, but we kept looking for ways to make it even faster. When we considered what these functions were doing internally, another optimization occurred to us. From a high-level point of view, these functions work with the screen as a complete unit: you read a screen in a single operation and you update it in a single operation. But the functions are implemented to iterate through the fields one at a time and do whatever is necessary to read or update that individual field. For example, it would take at least 3 seconds to read or write a screen containing 30 fields, assuming each field incurred a single network access of 100 ms. The solution was to enhance the SYSGUI device in BBj with a concept called batches. Batches allow you to assemble a group of SENDMSG() and/or CTRL() functions and execute them all at once, with a single network access. We rewrote all four functions to perform batch updates. This change makes a huge difference. No matter how many fields are on a given screen, a call to any of these functions translates to a single network request. After we added the batch syntax, we looked for other places in the application where we could combine multiple commands into a single batch. Any block of SENDMSG() or CTRL() functions is a candidate; we found several data-aware grids where the code could be optimized in this way. In each case, we were able to combine five SENDMSG() functions into a single batch.
Other Optimizations
We also found a few application strategies to eliminate some network accesses entirely. For example, some status messages can appear in either black or red. Instead of explicitly setting the color every time we update the field, the application keeps track of the current color and only resets it if the color needs to be changed. With all of these changes and testing in place, we finally got the performance that we wanted. When we started testing, screen transitions within the application were taking as long as 10 to 15 seconds. After working through these optimizations, we reduced this to a more acceptable 1 to 2 seconds. All of the changes we made in GUIBuilder and the application are performance-neutral when the application is run locally, so there's no need to maintain two versions of the application. If you keep these principles in mind when you're writing your application, it'll work well both locally and in a distributed environment.
Implementing the System
The approach we took began with our internal groups, including Accounting, Customer Service, Systems Administration and Sales. Our Accounting group had to deal with a major change in credit card processing, which was done through a third-party processor. We needed to make sure that we could track invoices and credit card payments all the way through the system. For orders that required shipment of CDs, we had to be sure that the items had shipped before the credit card processing was completed. This part of the b-Commerce application dealt mostly with learning more of the credit card processor's system and how to do all of these necessary activities. Our Systems Administration group made sure that the business logic, product listings, and serial number information and modification processes worked correctly. Through this, we discovered and added functions that our Customer Service staff did intuitively that weren't previously implemented in the system. We also needed to ensure the consistency and accuracy of orders entered through the automated system. To do this, we used a type of parallel systems test. Using a duplicate set of Customer data, Sales entered the orders into the b-Commerce system as well as continuing to write the orders manually. Our Customer Service staff entered the manual orders and then compared them to the b-Commerce orders entered by Sales. Once we were satisfied that the b-Commerce system was correctly generating orders, we went "live" using our main accounting system database. We tested the process for a couple of weeks ourselves and then put the system into the hands of our international distributors. The last and final stage was to open BASIS b-Commerce up to all BASIS Customers. We have initially implemented a level of functionality necessary to handle about 90% of the transactions we perform. But we plan many additional features to provide even more value to Customers as we evolve the application. Using BBj for our own Web-based business operations has turned out to be an exceptional testing situation. BASIS b-Commerce gives us the ability to accurately gauge real-world requirements for BBj in distributed computing. And as we continue optimizing BBj and its thin client interface, BASIS b-Commerce improves in its efficiency and capabilities.
| ||||||||
|
|
Copyright 1985-2000, BASIS International Ltd. All rights reserved. Terms of Use. BASIS International Ltd., 5901 Jefferson Street NE, Albuquerque, NM 87109-3432 505.345.5232, info@basis.com |
||||||||