Viktor and anyone else,

I'd like your opinion on something I've come up with that seems to work in my 
test box. What I've done is set things up so that instead of % thresholds I'm 
using a count of sent email. I fully expect the counting to not be 100% 
accurate, off by a couple of tens or even hundreds isn't a big deal. What I did 
was create the following tables:
DROP TABLE IF EXISTS domains;
CREATE TABLE domains(
   domain_id INT GENERATED ALWAYS AS IDENTITY,
   domain_name text,
   PRIMARY KEY(domain_id)
);

DROP TABLE IF EXISTS transport;
CREATE TABLE transport(
   transport_id INT GENERATED ALWAYS AS IDENTITY,
   transport_name text,
   PRIMARY KEY(transport_id)
);

drop table if exists send_counts;
create table send_counts(
   domain_id int unique,
   send_count int,
   CONSTRAINT fk_domains
      FOREIGN KEY(domain_id)
        REFERENCES domains(domain_id)
        ON DELETE CASCADE
);

drop table if exists thresholds;
create table thresholds(
   domain_id int,
   threshold int,
   CONSTRAINT fk_domains
      FOREIGN KEY(domain_id)
        REFERENCES domains(domain_id)
        ON DELETE CASCADE
);


drop table if exists matrix;
CREATE TABLE matrix(
   matrix_id INT GENERATED ALWAYS AS IDENTITY,
   domain_id INT,
   transport_id INT,
   PRIMARY KEY(matrix_id),
   CONSTRAINT fk_domains
      FOREIGN KEY(domain_id)
        REFERENCES domains(domain_id)
        ON DELETE CASCADE,
   CONSTRAINT fk_transport
      FOREIGN KEY(transport_id)
        REFERENCES transport(transport_id)
        ON DELETE CASCADE
);

So  tables w/ the destination domains, transport to use for said domain, count 
of emails sent to said domains, thresholds for the domains, and matrix table to 
join the domains and transport. I went a bit overboard w/ normalizing the 
tables. It probably could have all been put into one, but I'm also using this 
an exercise to play w/ postgress. Once all the data has been loaded into the 
tables it's ready to roll. What I probably should do is use a user defined 
function, but I found that I can put an update and select in the .cfg file and 
it works. I'm using this for the query:
query = update send_counts set send_count = case when send_count > threshold 
then send_count else send_count + 1 end from domains, thresholds where 
send_counts.domain_id=domains.domain_id and 
send_counts.domain_id=thresholds.domain_id and domain_name='%s'; select 
transport_name as "transport" from matrix m inner join domains d on 
m.domain_id=d.domain_id inner join transport t on m.transport_id=t.transport_id 
left join send_counts s on m.domain_id=s.domain_id inner join thresholds h on 
m.domain_id=h.domain_id where domain_name='%s' and send_count <= threshold;

There are some extra fields and joins in that select query as I was using it 
for testing stuff.

Here's what my test data looks like:
select domain_name,transport_name,threshold,send_count from matrix m inner join 
domains d on m.domain_id=d.domain_id inner join transport t on 
m.transport_id=t.transport_id left join send_counts s on 
m.domain_id=s.domain_id inner join thresholds h on m.domain_id=h.domain_id;
 domain_name |    transport_name     | threshold | send_count
-------------+-----------------------+-----------+------------
 gmail.com   | relay:[send.blah.com] |        15 |         16
 yahoo.com   | relay:[send.blah.com] |        25 |         20
(2 rows)

The small time test I did worked like a charm, but I'm only doing a single 
client sending, not at scale.

I'm interested in anyone's thoughts on what I might have overlooked that could 
come back to haunt me if I decide to roll this out anymore.

Thanks in advance.
Sean


-----Original Message-----
From: owner-postfix-us...@postfix.org <owner-postfix-us...@postfix.org> On 
Behalf Of Sean Hennessey
Sent: Wednesday, November 30, 2022 11:38 PM
To: postfix-users@postfix.org
Subject: RE: Is there an easy way to "warm up" a new sending IP w/ Postfix

Viktor,

I want to thank you a million for this. I finally read up on the docs and got 
this working. I'm still going to do some more in depth testing, but my quick 
little testing seems to be doing exactly what I wanted.

-----Original Message-----
From: owner-postfix-us...@postfix.org <owner-postfix-us...@postfix.org> On 
Behalf Of Viktor Dukhovni
Sent: Tuesday, November 29, 2022 3:44 AM
To: postfix-users@postfix.org
Subject: Re: Is there an easy way to "warm up" a new sending IP w/ Postfix

On Mon, Nov 28, 2022 at 08:57:37PM +0000, Sean Hennessey wrote:

> I searched the list archives and saw the thread of gradual shift of
> traffic from back in February of this year. That gives me some ideas,
> but that seems to be for all traffic, not a subset.
>
> I'd really like a way to send X% of gmail.com traffic to one relay and
> the rest to another relay. Ditto for a couple of other major ESP's
> like Yahoo, MS, etc...

If you're willing to spin up a small Postgres database (modulo typos on my part 
that should be easy to correct):

    query = SELECT U."transport"
            FROM (
                SELECT CASE WHEN floor(random()*100) <= T."weight"
                       THEN T."transport"
                       END AS "transport"
                FROM "transports" AS T
                WHERE T."domain" = '%s'
            ) AS U
            WHERE U."transport" IS NOT NULL;

Just populate a table:

    CREATE TABLE IF NOT EXISTS "transports" (
        "domain" TEXT PRIMARY KEY,
        "transport" TEXT NOT NULL,
        "weight" INTEGER NOT NULL
        );
    INSERT INTO "transports" ("domain", "transport", "weight")
    VALUES ( "gmail.com", "relay:[gmail-relay.example]", 99),
           ( "yahoo.com", "relay:[yahoo-relay.example]", 50),
           ... ;

And gradually lower the weights until, at weight 0, 99% of the traffic is 
direct to MX and just 1% of the traffic goes to the bypass relay and after that 
the row can be deleted.  Initially, at weight 99, all the traffic goes to the 
bypass relay.

If you want to specify a custom transport even after removing the relay, add a 
fourth (nullable) column and use that value in an ELSE clause of the CASE 
statement, in which case that value will be used when the bypass is not 
selected.

Keeping the Postgres database local to the MTA will improve performance and 
reliability.  I'd resist the temptation to centralise it, but that is an option 
if you're willing to have Postfix stall when a remote DB server is unreachable 
or slow, or can somehow avoid that.

It is not obvious to me, just at the moment, how to do this with the built-in 
Postfix randmap, pipemap, uniomap, ...

--
    Viktor.

Reply via email to