July 27, 2004 at 9:16 pm
Hi,
I have a project where I need to develop a solution to send e-mail message to thousands of recipients (approx. 100k or so). The recipients' e-mail addresses are stored in a SQL Server 2000 database table. The solution also needs to able to keep of track of which recipients read the message (Note: For this requirement, I'm thinking of including in the message body an "I have read this message" hyperlink which refers to an ASP page with code to track this)
Currently, there is a solution in place for sending e-mails. The solution is an ASP page that opens a recordset with all the e-mail addresses and uses the CDONTS server object to send the e-mail message to each recipient in the recordset.
The solution suffers from problems such as:
1) Script Timeout
2) Restart Issues (if an error occurs, the next time the script runs it does not know which recipients it already sent the message),
3) Also, Exchange Server's message queue gets backed up when it reaches an e-mail it cannot send.
The server environment is:
Exchange 2000
SQL Server 2000
What are some recommendations or thoughts on possible solutions (i.e. load balancing, etc.) to these problems?
Any help is greatly appreciated ....
JP
July 27, 2004 at 9:43 pm
You might try something along these lines...
The ASP page sends an email that Exchange recognizes in some way. Perhaps it drops it in a folder, etc. Exchange can fire an event using code to retrieve the list of email addresses and fire off messages one by one. If you're trying to have the ASP page generate the emails, you could increase the Script Timeout value, but I'm not thinking this is the best way to go unless you just don't have the ability to touch the Exchange server.
What you probably want to do is create some sort of tracking table for whenever you want to send emails are to be sent. When a process kicks off, it writes an entry into that table. As each email is sent in turn, a field is updated on that table. That way, should the process break, if you have to restart it, the process will read the table, see if there are any entries. If so, it'll pick up where it left off.
As far as getting the read receipt, that's a hard one. No matter what you do, some folks aren't going to click on the link. If you try and configure the email to go out with a delivery or read receipt, some won't come back. If you haven't already, communicate that while it's great in theory, in practice you aren't going to get very good results on this one unless the link is an acknowledgement for something mandatory.
K. Brian Kelley
@kbriankelley
July 28, 2004 at 10:04 am
Though many people complain about the xp_send/read mail functions, I have implemented a successful and reliable method of sending mails and processing the response. The nice thing is that if the process fails at any point, you have the repsonses still sitting in an exchange mailbox and can just restart the processing.
If you can trust the users to reply, leaving the original subjet line or contents in place, then this may work for you.
If you go the SQL Main route, I found that always starting your code with
exec master.dbo.xp_startmail eliminates issues with the process hanging.
July 29, 2004 at 7:23 am
One thing to try is to create a stored procedure that calls CDONTs and sends the mail. This way, you can schedule it to run as a job async and forget about calling it from asp or at least have asp initiate the job and return back to the client quickly.
You will also be able to update or track each recipient since you're in the SQL db.
I'm not at the office right now, so send me an email at msahoo@healthevolutions.com and I will be happy to send you the code to call CDONTs from SQL Server stored procedures.
July 29, 2004 at 8:33 pm
thank you everyone for your input ...
msahoo, could you please post in this thread your code sample on how to call CDONTS from a stored procedure. i know you would have use the SQL Server OLE Automation system stored procedures (i.e. sp_OA...) but i've never used those procs before.
thanks everyone for your help ...
JP
July 30, 2004 at 4:42 am
I wrote a mass mailing application of this sort a while back and the best way is to use a queing table - just have your ASP page write a single row into this table providing the details of the recipient, which message to send, any modifications to the standard message etc.
A VB app or something similar can then sit and every minute or so check the table and send out any emails for rows that are in the table. This way your ASP page provides instant response and with the VB app timed on 1 minute your customers will not notice that a queue is being used. This approach will handle however many emails you wish to throw at it, with it processing through the queue it will just keep churning away until it's done.
Dave.
July 30, 2004 at 5:04 am
I've also worked on a similar system recently. I implemented a mail queue in a sql server table then used a stored procedure with a cursor to iterate through the queue and send the mails. The queue was then updated depending on whether the mail was sent successfully or not. You can decide for yourself whether you want to keep a record of sending the mail as an audit trail.
You could call this SP from a scheduled task within SQL Agent to account for failures or the mail system, the overhead of checking for records still in the table would be minimal.
I would also recommend looking at xp_smtp_sendmail from http://www.sqldev.net. This will allow you to send HTML format messages nice and easily as I believe you're limited to plain text through xp_sendmail. The advantage of doing it through this xp is that it's all within SQL server, no need to go out to CDONTS yourself.
Tony
July 30, 2004 at 6:05 am
Our application sends about 15 - 20 k emails each day and it does it pretty smoothly ...Not many glitches
We have a Queue table which has a row inserted when an email needs to be fired. A sql stored proc run as a job scheduled to run every 2 minutes generates an xml and passes it on to a component which sends the emails and once the emails are sent there is a flag thats updated on a log table which keeps records of every email sent.
Ideally, what we have done is to eliminate the load on the SQL Server by placing the eMailing portion into components and sending xml with all the data rather than open connections.
Read Reciepts is not as easy as it seems. We havent been able to successfully handle that till now.
Cheers!
Arvind
July 30, 2004 at 7:54 am
If you can compose and deliver and HTML email, many systems use a small 1x1 pixel white image to track reads. They create a few million unique ones, mail the link inside the message and when the image is retrieved, they mark a read. They can also reuse the image at that point.
July 30, 2004 at 8:09 am
Please clearify: Exchange Server's message queue gets backed up....
We send close to 50k emails every morning, using Windows2003, CDONTS, Exchange and SQLServer. The scripe is written in VBScript and runs in about 1.5 hours. Exchange BARELY/RARELY hiccups.
What is your "sendusing" set to ? If you move from a pure ASP page to a .Net app, you can add better error/recoverability handing and automate this process quite easily.
July 30, 2004 at 8:41 am
Here's my code for calling CDONTs from SQL Server. I wrote this a long time ago, so it may not be the best code. Feel free to optimize it.
First, I created a table for configuration settings that are set during the procedure execution. See the CDONTs documentation for setting properties. Since we sell a commercial application, I needed to have the configurations for CDONTs be "configurable" per installation. If you don't, you may want to hard-code the stored procedure to set these properties.
CREATE TABLE [dbo].[tblCdoConfigProperties] (
[CDOPropID] [int] IDENTITY (1, 1) NOT NULL ,
[Property] [varchar] (255) NOT NULL ,
[Setting] [varchar] (255) NOT NULL ,
[PropertyType] [varchar] (50) NOT NULL ,
[Active] [bit] NOT NULL
) ON [PRIMARY]
Values in the table (in respective order to the columns above separated by a space)
1 http://schemas.microsoft.com/cdo/configuration/smtpserver Need.SMTP.Here varchar 1
2 http://schemas.microsoft.com/cdo/configuration/sendusing 2 varchar 1
8 http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout 10 numeric 0
Below is the SP. Note, I use it to send HTML email content to the users. Feel free to modify to suit your needs, of course. You will need to build a SP that builds your email and then calls this SP.
CREATE procedure prCDOEmail
( @To varchar(255),
@From varchar(255),
@Subject varchar(255),
@HTMLBody varchar(8000)
)
AS
DECLARE @object int
DECLARE @hr int
DECLARE @return varchar(255)
DECLARE @src varchar(255), @desc varchar(255)
-- Create an object.
EXEC @hr = sp_OACreate 'CDO.Message', @object OUT
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Set the initial properties of CDO. The properties are stored in
-- a table so the installation can be custom configured.
DECLARE @Property varchar(255)
DECLARE @Setting varchar(255)
DECLARE @NumericSetting numeric
DECLARE @PropertyType Varchar(50)
DECLARE CDOCursor CURSOR FOR
SELECT 'Configuration.Fields("' + Property + '").value', Setting, PropertyType
FROM tblCDOConfigProperties
WHERE Active = 1
open CDOCursor
FETCH NEXT FROM CdoCursor INTO @Property, @Setting, @PropertyType
WHILE @@FETCH_STATUS=0
BEGIN
IF(@PropertyType = 'numeric')
BEGIN
SELECT @NumericSetting = CAST(@Setting as numeric)
EXEC @hr = sp_OASetProperty @object, @Property, @NumericSetting
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT 'Error setting property: ' + @Property, hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
CLOSE CDOCursor
DEALLOCATE CDOCursor
RETURN
END
ELSE
PRINT 'set ' + @property + ' to ' + @setting
END
ELSE
BEGIN
EXEC @hr = sp_OASetProperty @object, @Property, @Setting
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT 'Error setting property: ' + @Property, hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
CLOSE CDOCursor
DEALLOCATE CDOCursor
RETURN
END
ELSE
PRINT 'set ' + @property + ' to ' + @setting
END
FETCH NEXT FROM CdoCursor INTO @Property, @Setting, @PropertyType
END
CLOSE CDOCursor
DEALLOCATE CDOCursor
-- Get the OLE reference of the object to set the fields of the CDO email
EXEC @hr = sp_OAMethod @object, 'Configuration.Fields.Update'
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Set To property.
EXEC @hr = sp_OASetProperty @object, 'To', @To
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Set From property.
EXEC @hr = sp_OASetProperty @object, 'From', @From
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Set Subject property.
EXEC @hr = sp_OASetProperty @object, 'Subject', @Subject
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Set HTMLBody property.
print 'Sending ' + cast(len(@htmlBody) as varchar(10)) + ' characters'
EXEC @hr = sp_OASetProperty @object, 'HTMLBody', @HTMLBody
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Send it off
EXEC @hr = sp_OAMethod @object, 'Send'
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
-- Destroy the object.
EXEC @hr = sp_OADestroy @object
IF @hr <> 0
BEGIN
EXEC sp_OAGetErrorInfo @object, @src OUT, @desc OUT
SELECT hr=convert(varbinary(4),@hr), Source=@src, <A href="mailtoescription=@desc">Description=@desc
RETURN
END
September 27, 2004 at 6:23 am
Hello everyone,
I have to prepare a application that captures subscriptions of users which they define in website and then send them the notifications whenever a match occurs as when required daily,weekly or monthly.
I have gone throuch many doc ,regarding this but i am confused which technology to use.I have gone through SQL Notification Services,SQl reporting Services , Sending mail through CDONTS.
Can somebody guide me which technolgoy to use.
Thanks in advance.Its urgent waiting for ur replies.
September 27, 2004 at 6:25 am
Hello everyone,
I have to prepare a application that captures subscriptions of users which they define in website and then send them the notifications whenever a match occurs as when required daily,weekly or monthly.
I have gone throuch many doc ,regarding this but i am confused which technology to use.I have gone through SQL Notification Services,SQl reporting Services , Sending mail through CDONTS.
Can somebody guide me which technolgoy to use.
Thanks in advance.Its urgent waiting for ur replies.
Viewing 13 posts - 1 through 12 (of 12 total)
You must be logged in to reply to this topic. Login to reply