I have a use case where I need to process about 80,000 emails per day. The client has an existing process that takes about 1/sec per email and is running out of time before the next batch comes in. I implemented a solution using the MailKit library and I am trying to implement some parallel tasks using TPL. I tried both a Pop3 client and IMAP client and the IMAP client seems to be faster. Plus, since IMAP supports multiple clients connecting at the same time so I figured that would be better for TPL.
I tested the code with a small sample of 100 emails and trying different numbers of parallel tasks. The results are below.
I'd appreciate any advice on my code below or other improvement suggestions on how to make it faster.
private static int _num_of_threads = 1;
private static int _num_of_messages = 100;
public override async void DoWork()
{
try
{
IList<UniqueId> uids = new List<UniqueId>();
uids = GetUids();
//shrink the list for now
uids = uids.Take(_num_of_messages).ToList();
//create {_num_of_threads} lists
List<List<UniqueId>> list_of_list_of_uids = new List<List<UniqueId>>();
list_of_list_of_uids = SplitUids(uids);
// create the TPL tasks
Task[] tasks = new Task[_num_of_threads];
for (int index = 0; index < tasks.Length; index++)
{
tasks[index] = Task.Factory.StartNew((Object obj) =>
{
int i = (int)obj;
GetMessages(list_of_list_of_uids[i]);
}, index);
}
Task.WaitAll(tasks);
}
catch (Exception ex)
{
}
}
private List<UniqueId> GetUids()
{
IList<UniqueId> uids = new List<UniqueId>();
using (var client = new ImapClient())
{
try
{
client.Connect("<emailserver>", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate(<username>, <password>);
client.Inbox.Open(FolderAccess.ReadOnly);
uids = client.Inbox.Search(SearchQuery.All);
}
finally
{
if (client.IsConnected)
{
client.Disconnect(true);
}
}
}
return uids.ToList();
}
private List<List<UniqueId>> SplitUids(IList<UniqueId> uids)
{
List<List<UniqueId>> list_of_list_of_uids = new List<List<UniqueId>>();
for (int list_index = 0; list_index < _num_of_threads; list_index++)
{
List<UniqueId> list_of_uids = new List<UniqueId>();
list_of_list_of_uids.Add(list_of_uids);
}
// should have {_num_of_threads} lists now
//now spread the uids across the lists
int list_iterator = 0;
for (int uid_index = 0; uid_index < uids.Count(); uid_index++)
{
list_of_list_of_uids[list_iterator].Add(uids[uid_index]);
list_iterator++;
if (list_iterator == _num_of_threads)
{
list_iterator = 0;
}
}
return list_of_list_of_uids;
}
private async Task<int> GetMessages(IList<UniqueId> uids)
{
int count = 0;
using (var client = new ImapClient())
{
try
{
client.Connect("<emailserver>", 993, SecureSocketOptions.SslOnConnect);
client.Authenticate(<username>, <password>);
client.Inbox.Open(FolderAccess.ReadOnly);
foreach (UniqueId uid in uids)
{
var message = client.Inbox.GetMessage(uid);
if (message != null)
{
// process the email
// process aggregates a bunch of data and sends the result off to a web service to be put into a DB.
count++;
}
}
}
finally
{
if (client.IsConnected)
{
client.Disconnect(true);
}
}
}
return count;
}
Best Result:
1 Thread = ~54 seconds
2 Threads = ~21 seconds
3 Threads = ~15 seconds
4 Threads = ~14 seconds
10 Threads = ~14 seconds