views:

78

answers:

4

I would like to create/use a system-wide independent universal 'counter object' that can be called via COM in a thread-safe manner.

The counter object will be passed an ID to identify which counter to return, handle the counting, 'persist' the count (occasionally), have reasonable performance (as fast as possible) perhaps capable of 1000 counts per second or better (1mS) and be accessible cross-process/out-of-process. The current count status must be persisted between object restarts/shutdowns.

The counter object is liklely to be a 'singleton' type object implemented in some form of free-threaded dictionary, containing maybe 10 counters (perhaps 50 max). The count needs to be monotonic and consistent, (ie: guaranteed unique sequential values).

Each counter should have a few methods, like reset, inc, dec, set, clear, remove. As a luxury, I would like to have a variable-increment (ie: 'step by' value). To support thread-safefty, perhaps some sorm of critical-section or mutex call. It just needs to return a long/4byte signed integer.

I really want something that can be called from anywhere, including VBScript, so I figure COM is my preferred solution.

The primary use of this is for database keys. I am unable to use autoinc or guid type keys and have ruled out database-generated counting systems at this point.

I've spent days researching this and I have really struggled to find a solution. The best I can find is a free-threaded dictionary object that can be instantiated using COM+ from Motobit - it seems to offer all the 'basics' and I guess I could create some form of wrapper for this.

So, here are my questions:

  • Does such a 'general purpose counter-object already exist? Can you direct me to it? (MS did do an IIS/ASP object called 'MSWC.Counter' but this isn't 'cross-process'/ out-of-process component and isn't thread-safe. (but if it was, it would do!)

  • What is the best way of creating such a Component? (I'd prefer VB6 right-now, [don't ask!] but can do in VB.NET2005 if I had to). I don't have the skills/knowledge/tools to use anything else.

I am desparate for a workable solution. I need specific guidance! If anybody can code something up for me I am prepared to pay for it.

Update:

  1. Whats wrong with GUIDs? a) 16bytes if I'm lucky (Binary storage), 32+bytes if I'm not (ANSI without formatting) or even worse(64bytes Unicode). b) I have an high-volume replicated app where the GUID is just too big (compared to the actual row data) and c) the overhead of indexing and inserts d) I want a readable number! - I only need 4 byte integer, so why not try and get that? I know you will say that disc-space is cheap, but for my application the cost is in slow inserts, and guids don't help (and I have tried/tested) but would prefer not to use if I have a choice.

  2. Autonumber/autoincs are evil: a) don't get the value until after the insert, b) session specific, c) easy to lose/screw up on a table alter, d) no good for mutli-table inserts, (its not MS-SQL Svr) plus I have a need for counters outside my DB...

+1  A: 

A database engine is already very good at generating unique primary key values for a dbase table. Either by marking the column auto-increment or by using a Guid. Trying to create your own is a grave mistake. System wide is just not wide enough, it fails miserably when your app grows and more than one machine starts using the database.

Nevertheless, you can get what you want in VB6 by creating a COM server. It's been to long, I forgot the exact names of the project options, something resembling "single use".

Hans Passant
I've updated my question to explain why I would prefer not to use ai or guids. I have been looking at COM servers, GlobalSingleUse EXE server may give me what I want, but need some reassurance.
andora
You are not going to get reassurance here, nobody thinks this is a good idea. In is in fact a *very* lousy idea, one you'll regret deeply some day.
Hans Passant
Strong words! Nobody? Lousy? really? Is it that clear cut? Just suppose I didn't want this for a database, but I wanted a counter for say a collection of invoices spread over a number of threads or processes, what would you recommend then? It seems perverse to me that I cannot just generate a simple sequence of numbers on a PC that just happen to end up in a database. It can't be that hard or that regrettable?!
andora
Hmya, you are completely ignoring the fact that your dbase may service multiple web servers or client PCs some day. But go ahead, it's not my funeral. I gave you a solution, it ought to work for you.
Hans Passant
Thanks anyway. I'm not ignoring the possibility, its just not what I'm asking about.
andora
I've explored 2 possibilites, both of which deliver a working solution: 1) Wrap a DB call with explicit locks via a COM component and 2) Implement a mutex/semaphore locked dictionary COM+ component.
andora
+1  A: 

By the sound of it, what you're looking to create is an ActiveX exe. They run in their own process but can be accessed from any other process by instantiating an object from it as though it is just another COM object. It handles all the marshaling necessary to sync its internal thread with the threads of any process calling it. Since all you planning on using is ints, there's no need to worry about the thread safety of objects passed between the threads.

More than likely you can use the MSWC.Counter object within that ActiveX exe and let it do the counter work.

Corin
This is what I thought. Thanks. Can you elaborate on the thread safety? I guess Ints are 'atomic' but are the operations to increment or locate in an array safe? Can I really 'not worry' at-all?
andora
It all depends on how you transfer the information out of the ActiveX exe. If you just pass ints, you're fine. If you're passing arrays, then you'll want to make sure you have a way to copy the array safely.
Corin
Just to be clear, only a single count value will be returned. I'm no expert with COM/Threads, but only an INT would be marshalled cross process, ie the current value. I was thinking more 'internally' to the server as the counter(s) would be held in a dictionary list/array ctr1,ctr2,ctr3, etc, each with its own value and one of them would be incremented and returned. What I am not clear on is how/if the dict-object could 'miscount' internally if it was interrupted, and what I might have to do to 'protect it' with a critical-section or mutex type or similar lock. And if it will work at all!
andora
To be absolutely sure that the count is accurate (ie two requests can't get the same number), you'll want to make sure the request is wrapped properly with a mutex.
Corin
A: 

The whole thing sounds like a twisted idea, so why should I not add another twisted one. :P

Host an old-skool ASP page.
You can use Application.Lock with a counter then, just like in the sample.
Added benefit: use it from any platform/language. (e.g. other HTML pages with XMLHttpRequest. :)
If you save the value at say every 100th request to a file, you do not even have to worry about IIS resets.
Just set the starting value to last saved value + 100 in Application_OnStart. :P

andras
Interesting, but don't want to rely on ASP. Thx for the suggestion.
andora
+1  A: 

I have implemented a similar solution implemented as a REST web service - accessible from any technology that supports http.

Simple c sharp backend implementation using a singleton pattern and will scale nicely under IIS.

James Westgate
This is a useful possibility, but doubtful that I could use right now. Thx anyway +1
andora