RSS

Monthly Archives: June 2011

Pyzmq – not a lot of best practices

ZeroMQ is a Message Queue (MQ) framework that plainly works. Two of the most interesting elements are 1) ZMQ supports a number of client languages; 2) the broker (generally an application that exists to route traffic from a producer to a consumer) is left to the programmer to implement.

If you’ve read or used any of the other MQs or if you’ve done some interprocess communication (IPC) before you probably have a good or general idea how this is supposed to work. RabbitMQ does a really good job naming the different patterns and keeping the list to something manageable. While the ZMQ doc is long, detailed, absent of examples for each of the client languages, examples are buggy or old, the examples are simple; but they have many more patterns than RMQ.

One idea that keeps getting trapped in my head is; How do I send a request to the broker and wait for a response. And if the response does not arrive, then what? Basically I’m looking for a best practice here.

In my application design I have the following stack:

json_rpc -> broker -> worker -> remote_http
  1. a user application sends my stack a json-rpc call
  2. the call is forwarded to the broker
  3. the broker routes the request to a worker
  4. the worker forwards the request to a remote service provider
  5. and whatever response is returned… bubbles up through the call stack

So in my json-rpc application I have a module that waits for requests, the message is authenticated, and the input data is validated. Since the response time can be between 250ms and 90secs we’ll keep the socket open and wait for the reply. The challenge here is getting the json-rps app to make the request, detect errors, handle certain errors, and forward the request to the broker, wait for a reply, parse the response message, and return to the caller.

Here is the pseudo code:
retry = 3
zmq.send(socket, request, zmq.NOBLOCK)
while True:
    # wait 7 seconds between boll timeouts
    socks = dict(poller.poll(7000))
    try:
        if socks.get(service) == zmq.POLLIN:
            # we got a message
            # NOBLOCK here almost makes no sense other than we want to get
            # all errors and we do not want to block at all
            # any errors will generate an exception
            # we could assert against an empty response but why?
            rsp = zmq.recv(socket, zmq.NOBLOCK) 
        else:
            # timeout
            retry = retry - 1
    except zmq.core.error.ZMQError, e:
        retry = retry - 1
        if zmq.EFSM == e.errno:
            # wrong state
            pass
        elif zmq.EAGAIN == e.errno
            # no data
            pass
        else:
            retry = 0
        if retry <= 0:
            # reconnect to the broker
            service.close()
            . . .
            retry = 3
            continue

The thing to keep in mind is that ZMQ will not return from a send or receive unless something good happened. That means that you can expect an exception to be fired if your message did not get out, the response was not received, some other app in the stack restarted and thus changed the state of the socket. This makes sending messages reliably very simple… even though my post is even simpler than than.

 
Leave a comment

Posted by on 2011/06/29 in Uncategorized

 

NoSQL for your next project?

I keep keep going back and forth on the whole idea of NoSQL and it bothers me to no end. On the one side the idea of sharding the data at the server level is appealing. Then there are the Key/Value databases and then the document versions. They are graph databases, object databases and a few in between. But then, as always, there is this reality check.

As wonderful as NoSQL would appear to be there is no single use-case that would seem to make it/them the obvious choice. And this is no more obvious as I stumble around a new project I’m considering… an open source merchant gateway and an open source issuing system. I would really like to have one and only one system that I could use for everything but that does not seem possible.

For example; in order to deal with the protocol impedance I need a fifo queue of some kind. I like redis for this as it also has a TTL so that old data is simply removed from the queue. It also has the notion of fields in the value so that a single record can actually represent a dictionary or an array; making a useful container for the message components. (converting an ISO-8583 or variables from a POST into a dictionary is fun and useful). Also, since there is a fifo queue the transaction results and logging can be pushed into a different channel for aggregation and processing later.

In both the issuing and gateway systems the transactional part is considered OLTP system and OLTP systems benefit greatly from hash tables like those that redis provides. However, redis is useless when it comes to reporting, mapreduce, partitioning, and performance when the dataset approaches available memory limits. So it would seem that it would be useful to have a SQL or other storage mechanism to store all the persistant data, a queue for all of the logs and transactions, and a hash to act as a cache for the data from the primary store. The challenge here is that the first time there is a miss on the account data on the cache the compute node will have to go to storage to get the account data. This can be costly and 2x machines will negatively effect the sigma score. And then system recovery is going to be harder even if the data is replicated around the systems. (expire/TTL does not work as expected on replicated systems.)

The NoSQL Databases website claims that there are 122+ NoSQL databases. I just ran over a bunch of them and they all left me wanting something better… or at least feeling that I needed to go back to an RDBMS. (read PostgreSQL)

Some requirements:

  • a queue for message logging that is persistant and replicated
  • a queue for transaction logging account info back to the primary storage
  • a queue for transaction requests that can and will expire, however, the expiration will trigger a log entry
  • a cache that represents the account data from the primary storage
  • a cache that represents the config data from the primary storage that is reloaded on-demand
  • flush from cache when the transaction entry is recovered from the transaction log
  • evaluate the mapreduce hit/miss when the transaction log is processed or mapreduce once a day or hour
  • import the data from the cache into a detached slice until the data is ready for consumption. Then attach it. (Postgres)
One last comment.  Replication is a pain. Specially when you are exporting transactions this way. Given the amount of time it takes to sync the data, specially with transaction bursts, the overall system can and will experience slowdowns. It gets worse when the users are trying to interact with the data. On top of that importing CSV is so much better. And finally, doing the imports when the users are not using the system means that the data is stale but accurate.

I’m going to build a test harness made from redis+mongodb and redis+postgres… just for fun and testing.

 
Leave a comment

Posted by on 2011/06/23 in Uncategorized

 

A new approach : HamsterDB

Revisiting my favorite subject again, credit card processing, the hamsterDB’s description on the NoSQL website triggered an alarm.

hamsterDB – (embedded solution) ACID Compliance, Lock Free Architecture (transactions fail on conflict rather than block), Transaction logging & fail recovery (redo logs), In Memory support – can be used as a non-persisted cache, B+ Trees – supported

The key words being “lock free”. In any typical CC issuing system you can expect to see transaction times from 50 to 500ms depending on the amount of work the authorization system has to perform, DB latency and locking.

Typical transaction workflow looks like some code that just tries to get some data from the DB, do some work, get some more data from the DB and do some more work. And while performing I/O with the DB you always have to be ready for a failure. Typical failures are deadlocks, consistency because another process updated a record and so on. And when you think about the breadcrumbs and trying to recover from these failures it simply makes the code more complex.

update account set balance=balance-10.00, version=version+1 where ccnumber=? and version=11;

With the hamster approach you can and should get all the data that you need from the DB upfront at the beginning of the transaction. Keep in mind that in some use-cases this data could be prohibitively large so it’s best to completely understand the scope. Then do the workflow as you would normally… leaving you with a set of DB updates/inserts that need to be executed. So execute them.

Now if anything goes wrong you have choices. 1) retry the DB changes from the last step; 2) retry the workflow from step two; 3) retry the entire transaction. It simply depends on the nature of the write failure and what you determined was the best recovery.

And here’s why this is better. Given the performance profile for an authorization (50-500ms) and the timeout that is permissible by the association (10-45seconds depending in the trantype) you can retry this transaction internally almost any number of times in order to get a positive response… providing the error was internal and not hardware, network ete related.

One other thing that did not escape my eye. Kevin Smith (formerly from Riak) is the erlang client maintainer. Optional encryption(great for PCI)

On the downside there is no replication, sharding, python, perl, or traditional C. However, the approach would be interesting for other platforms… almost there.

 
1 Comment

Posted by on 2011/06/23 in credit cards, database

 

Tags: ,

Current News and Updates

Google released version 1.5.1 of App Engine. They added some significant APIs and features, however, in my mind it’s missing a GO update.

Tornado has been in version 1.2.1 for a long time… and the developers just release version 2.0. (download here) Looking at the release notes there are 3 major updates and several minor. Many of the minor updates are prerequisites. The most impressive will undoubtedly be support for python 3.2. However there may be some minor backward compatibility issues.

 
Leave a comment

Posted by on 2011/06/22 in beta, ProgLang, updates

 

Tags: , , , ,

Should Go replace my use of Python?

Here is an interesting post that posited the question in my title: Experience porting 4k lines of C code to go http://bit.ly/jm0Qws

There are a lot of reasons to use GO. I like that it’s from Google but I don’t like that there is a release often approach. I need something that is a little more stable than that. Granted this offers some justification for deploying packages and the like and using goinstall in order to deploy and update packages as new releases of GO are made available. There is also something to be said about the monolithic codebase, however, that flies in the face of this deploy approach.

But I like the compiled performance, channels and the wealth of packages (It needs more like a performant web framework, templates and production ready database adapters.)

Go, while cool, is still a little half baked. Where python and perl are still up to the challenge.

 
Leave a comment

Posted by on 2011/06/22 in ProgLang

 

Tags: , , ,

“The Network Is the Machine” : Erlang is not all that

I like erlang and I like it most because it solves a number of problems, however, the problems that I think it solves in general application development are not the kinds of problems that most erlang programmers want to solve. For the [sp: life] live of me I cannot understand why erlang programmers would implement a database like Riak. It’s a complicated undertaking and frankly considering how deep the callstack has to be at times it does not seem practical without a real debugger.

As I consider the amount of work that it takes to implement a single credit card transaction I realize that the entire callstack is going to consist of a few thousand instructions regardless of the language. The hardest part of a credit card transaction is the DB record versioning and not the actual in-memory workflow.

So then we start talking about the threading and IPC. MEH. I no longer care about that stuff. Not even for a second. With libraries like ZMQ “we” should reconsider how we allow processes to communicate. Modern MQs are fast, reliable, persistent, and easy.

Finally, If you have a transaction that takes a predictable number of machine cycles (specially in the context of a CC transaction) then executing the transactions synchronously via a fixed number of workers will have less overhead than each transaction being launched at the same time. As light as they may be there is still overhead. O(1)+1 still has a +1 and at some time…. say after 1M transactions they will count for serious performance.

[update 2011-06-22: When the machine is idle then parallel execution and all the thread happiness in erlang makes sense, however, when the machine is busy then single process execution makes the most sense as there is no overhead no matter how small. ie; if you have 100K transactions you still have 100K worth of work to complete. The mean execution time will be higher just because of the latency and overhead but on the hole there is no real advantage. see nodejs as a partial example.]

[update 2011-08-24: I get it and it was not because I was talking to the CTO at Riak. Two days ago I was looking at the connection pool to a Postgres DB. That's when I realized that an erlang implemented database... at least for socket(s) and systems with long runtimes like connection pooling; was a very strong use-case for erlang.]

 
Leave a comment

Posted by on 2011/06/21 in nosql, ProgLang

 

Tags: , , , ,

The Really Big O

Get your mind(s) out of the gutter. I’m actually thinking about benchmarks not bed-marks.

Back in the olden days we used to refer to a program or algorithm’s performance profile in terms of o-notiation. I’m pretty sure that most computer scientists still follow this montra and for the most part it’s probably still true.

So if it is the case that o-notation is still a real form of measure then why do most languages have different performance profiles while they perform the same work. For example 100K compares is the same when it all boils down to the assembly instruction that makes the decision:

CMP    AX, BX
JE     #SAME_VALUE

When it comes down to it every language makes decisions the same way. So why such different profiles. I say again.

First of all I think that the trouble lies in the libraries. I’m not convinced that the same care id put into every library so that the minimal number of instructions is executed. The reality is… how much code needs to execute on either side of the instruction above to actually do the work in the target language?

Secondly not all JITs are equal. And while that’s good for som languages you’d think that at some point they’d all use the same JIT. But then I’d be complaining about benevolent dictators like Oracle. I really do not like the JDK being used for Scala, Clojure and others. While they are bolted on nicely… They simply inherit everything from the JDK and whatever Java libraries you care to use. As evidenced by Lift. Which seems to depend heavily on Jetty. And if the purpose of the language is to get the benefits of a functional language… using Java libs directly would seem to defeat the purpose.

Finally whatever you are trying to accomplish in a single transaction is not unlike the CMP above. The number of comparisons over the transaction is no different between the many language. So whichever language you select it should have more to do with the overal impedance with the problem scope instead of the lonely intellectual islands.

 
Leave a comment

Posted by on 2011/06/21 in ProgLang

 

Tags: , , ,

BerkeleyDB in Payments

BerkeleyDB is awesome… but I liked it better when it was a part of SleepyCat and not Oracle. I hope that Oracle does not bury the product and that it gets the attention it richly deserves.

A number of years ago, while I was working for WildCard Systems, I was designing an authorization system that had a few constraints. The first was that everything was deployed on Windows and second that the DB was going to be MS SQL Server and as a side effect of moving requirements from the sales team I was forced to implement the business rules as stored procedures. At the time SQL Server did not have a replication system and we were still running on souped up PCs pretending to be servers.

So I built my own replication engine. That failed. And then I tweaked it… it was OK for a while… until the transactions started to show up. Over the years we tried several, including Microsoft’s version too. They all failed one way or another. But I digress.

At some point everything was moved to enterprise class DELL hardware including a SAN from EMC. And we had some new/serious execs running the show. So now we were doing things like performance testing and peak season preparation and so on. And then the sad news arrived. We were only able to perform 25TPS, sustained. I was dejected. In later years I read about SAN broundouts on EMC hardware. And then there was the in-house replication. I was completely dejected as the system I built for First Data was doing over 100TPS; but then First Data was running on Sun hardware and Oracle and we did not have the budget to compete yet.

Some time shortly after an “Oracle vs SQL Server” meeting and Oracle was buried. I left WildCard. But the idea of developing an authorization system that could perform had captivated me. So I started reading. Finally I read an article from the CEO of SleepyCat. It so inspired me to implement some test programs using BerkeleyDB. In the end I was able to perform 1700TPS on very modest hardware doing almost a complete simulation of a transaction including reads and writes. Granted I had not included replication but I was able to test the basic premis.

I left the experiment with the feeling that BDB could do the job… and now that BDB is part of the NoSQL revolution, willing or not, it seems that maybe it deserves some more attention. So I’ve been thinking about them again… but now I don;t think that it’s a contender. Replication is left to the programmer to implement and if you ask me that sort of programming is highly specialized and demands that experts implement it. So I do not think BDB is a good match at all.

 
Leave a comment

Posted by on 2011/06/21 in database

 

Tags: , , , , , ,

Redis In Payments

There are a number of hurdles for the merchant checkout/shopping-cart to overcome when accepting credit card transactions. There are a number of obvious and outwardly facing challenges like:

  • PCI-DSS
  • Acquirer contracts
  • Shopping Cart
  • Banking
  • requirements – payments, recurring payments

Once you make it past, not so technical speed bumps, there are a number of implementation details that follow. On the one hand there is the user experience and how that is implemented by the website; and then there are the many acquirers and the different protocols and payload formats. This is usually referred to as transaction impedance.

 

What does this mean? How is that implemented in Redis?

Let’s start with the user experience. At some point the user will want to complete the purchase by providing some credit card information that you are going to use to send to an acquirer for processing. Given the number of ways this can be accomplished the best way will be an internal implementation using an iFrame. This way you can encapsulate and reuse the checkout in multiple places within your app.

The iFrame will then POST via REST or Ajax type message to the URL that provided the iFrame. The form data should be validated here and then forwarded to the message broker. I’m suggesting RestMQ is a good option as it uses Redis. The message is put on the message queue. Shortly thereafter a worker daemon that has been blocking on an empty queue will awaken, pull a message from the queue, reformat the message for the acquirer and forward the message to the Acquirer.

Here is where it get’s tricky. Depending on the protocol either the worker is going to wait for a response from the Acquirer in response to the request or the worker is going to move on to the next message in the broker’s queue. This depends on the protocol with the Acquirer. If the Acquirer is implemented in REST or HTTP then the worker can simply wait. Of course there can be as many workers for as many simultaneous connections the acquirer will permit.

On the other hand, many acquirers like to use a single socket and process the transactions in an asynchronous fashion. In which case you’ll need two threads and a cache for the transactions in flight. I know that’s a tough concept…. here goes…

A worker thread pulls a message from the queue, assigns a UUID, stores the transaction in a hash with an expiration date, then writes the message to the async socket.

Some other thread is blocking while reading from the remote socket.  When a response is received, the working will queue the response.  Another worker will read from that side-queue, locate the UUID, get the request from the cache, parse the response, construct a response for the user checkout and then forward the response to the user’s iFrame.

WOW… and now the interesting stuff.  Since the messages in the queue and cache can have expiration dates transactions that timeout can be allowed to timeout all over the system without adversely effecting the overall performance of the system. Historically transaction nodes have allowed transactions to timeout rather than send back errors in response to transactions that timeout. The logic required to insure that the error responses make their way around the “system” are costly. And in fact have demonstrated that users get nutty with the “retry” and they can essentially DDOS your payment system.

This is a pretty exciting design. Tornado and Cyclone use epoll and address the C10K problem nicely. Redis can perform 100K writes per second. Daemon tools can handle keeping the system alive. Redis offers some replication although it is master-slave so HA is still possible. Since this is a cluster solution it is possible to distribute the transaction load over several servers. However, in a cluster arrangement you may need to route the transactions (cluster affinity) so that the same card numbers follow the same route… should there be any sort of stand-in processing, duplicate transactions, etc… you want to have the latest information possible. Implementing routing via cluster affinity is another use-case for Redis as you can store the routes and then replicate that data and read the record from the slaves. With any luck this will be faster than an errant user. But you still have to keep an you out there for evil does… so a good blacklist is also helpful.

Some elements I left out… end of day batch file, recurring transactions, reporting, and customer care.

[UPDATE: Redis does not like combining EXPIRE and replication. The EXPIRE can have unpredictable results when executing queries against the slave(s). OK, so we have learned that FIFOs and Caches are useful in MQ broker construction and as part of the impedance mismatch correction. Redis/RestMQ seem to be strong tools.]

 
Leave a comment

Posted by on 2011/06/21 in Uncategorized

 

Tags: , , , ,

membase + couchDB = couchbase : Why?

Just a short note because I have some reading to do. In the back of my mind there are echos of membase being a ram-only cache-like db and that there were forks and the like that implemented the same APIs and had persistance. This guy does a good job breading things down, however, he gives membase 200K/sec and does not say anything about Redis’ performance. So while many of the elements that I would use for comparison are there they are not equally presented. Furthermore there is still a question in my mind about membase and it’s persistance that he suggests.

So many questions.

The biggest doubt I have is the title of this article. From the link in the article membase appears to have been absorbed into the couchbase project and so it does not exist on it’s own. Not to mention that membase.com redirects to couchbase.com. The same can be said for membase.org and couchbase.org.

Then primary use-case for membase is caching of data from any traditional DB, so where is the benefit of merging with couchbase?

 
Leave a comment

Posted by on 2011/06/21 in database

 

Tags: , , ,

 
One Page Docs

Creating a library one page at a time.

One Page Bugs

Reducing the friction of writing and fixing bugs or features.

Follow

Get every new post delivered to your Inbox.

Join 223 other followers