Browse Source

Add TTL and retain probability for DSL prioritize dials

Rod Hynes 1 month ago
parent
commit
518121e162

+ 4 - 0
psiphon/common/parameters/parameters.go

@@ -556,6 +556,8 @@ const (
 	DSLFetcherGetOSLFileSpecsMaxCount                  = "DSLFetcherGetOSLFileSpecsMaxCount"
 	DSLPrioritizeDialNewServerEntryProbability         = "DSLPrioritizeDialNewServerEntryProbability"
 	DSLPrioritizeDialExistingServerEntryProbability    = "DSLPrioritizeDialExistingServerEntryProbability"
+	DSLPrioritizeDialRetainFailedProbability           = "DSLPrioritizeDialRetainFailedProbability"
+	DSLPrioritizeDialPlaceholderTTL                    = "DSLPrioritizeDialPlaceholderTTL"
 	ServerEntryIteratorMaxMoveToFront                  = "ServerEntryIteratorMaxMoveToFront"
 	ServerEntryIteratorResetProbability                = "ServerEntryIteratorResetProbability"
 
@@ -1192,6 +1194,8 @@ var defaultParameters = map[string]struct {
 	DSLFetcherGetOSLFileSpecsMaxCount:                 {value: 1, minimum: 0},
 	DSLPrioritizeDialNewServerEntryProbability:        {value: 0.5, minimum: 0.0},
 	DSLPrioritizeDialExistingServerEntryProbability:   {value: 0.25, minimum: 0.0},
+	DSLPrioritizeDialRetainFailedProbability:          {value: 0.0, minimum: 0.0},
+	DSLPrioritizeDialPlaceholderTTL:                   {value: 24 * time.Hour, minimum: time.Duration(0)},
 
 	ServerEntryIteratorMaxMoveToFront:   {value: -1, minimum: -1},
 	ServerEntryIteratorResetProbability: {value: 1.0, minimum: 0.0},

+ 10 - 0
psiphon/config.go

@@ -1116,6 +1116,8 @@ type Config struct {
 	EnableDSLFetcher                                *bool    `json:",omitempty"`
 	DSLPrioritizeDialNewServerEntryProbability      *float64 `json:",omitempty"`
 	DSLPrioritizeDialExistingServerEntryProbability *float64 `json:",omitempty"`
+	DSLPrioritizeDialRetainFailedProbability        *float64 `json:",omitempty"`
+	DSLPrioritizeDialPlaceholderTTLSeconds          *int     `json:",omitempty"`
 
 	ServerEntryIteratorMaxMoveToFront   *int     `json:",omitempty"`
 	ServerEntryIteratorResetProbability *float64 `json:",omitempty"`
@@ -2939,6 +2941,14 @@ func (config *Config) makeConfigParameters() map[string]interface{} {
 		applyParameters[parameters.DSLPrioritizeDialExistingServerEntryProbability] = *config.DSLPrioritizeDialExistingServerEntryProbability
 	}
 
+	if config.DSLPrioritizeDialRetainFailedProbability != nil {
+		applyParameters[parameters.DSLPrioritizeDialRetainFailedProbability] = *config.DSLPrioritizeDialRetainFailedProbability
+	}
+
+	if config.DSLPrioritizeDialPlaceholderTTLSeconds != nil {
+		applyParameters[parameters.DSLPrioritizeDialPlaceholderTTL] = fmt.Sprintf("%ds", *config.DSLPrioritizeDialPlaceholderTTLSeconds)
+	}
+
 	if config.ServerEntryIteratorMaxMoveToFront != nil {
 		applyParameters[parameters.ServerEntryIteratorResetProbability] = *config.ServerEntryIteratorResetProbability
 	}

+ 2 - 2
psiphon/dataStore.go

@@ -2701,7 +2701,7 @@ func dslLookupServerEntry(
 	return serverEntryID, nil
 }
 
-// dslPrioritizeDialServerEntry will create a DSLPendingPrioritizeDial
+// dslPrioritizeDialServerEntry will create a DSLPendingPrioritizeDialTimestamp
 // placeholder dial parameters for the specified server entry, unless a dial
 // params already exists. Any existing dial param isn't unmarshaled and
 // inspected -- even if it's a replay past its TTL, the existence of the
@@ -2724,7 +2724,7 @@ func dslPrioritizeDialServerEntry(
 	}
 
 	dialParams := &DialParameters{
-		DSLPendingPrioritizeDial: true,
+		DSLPendingPrioritizeDialTimestamp: time.Now(),
 	}
 
 	record, err := json.Marshal(dialParams)

+ 58 - 25
psiphon/dialParameters.go

@@ -177,8 +177,8 @@ type DialParameters struct {
 	steeringIPCache    *lrucache.Cache `json:"-"`
 	steeringIPCacheKey string          `json:"-"`
 
-	DSLPendingPrioritizeDial bool `json:",omitempty"`
-	DSLPrioritizedDial       bool `json:",omitempty"`
+	DSLPendingPrioritizeDialTimestamp time.Time `json:",omitempty"`
+	DSLPrioritizedDial                bool      `json:",omitempty"`
 
 	quicTLSClientSessionCache *common.TLSClientSessionCacheWrapper  `json:"-"`
 	tlsClientSessionCache     *common.UtlsClientSessionCacheWrapper `json:"-"`
@@ -233,11 +233,14 @@ func MakeDialParameters(
 	// functions need to be updated when, e.g., new TLS obfuscation
 	// parameters are added.
 
+	currentTimestamp := time.Now()
+
 	networkID := config.GetNetworkID()
 
 	p := config.GetParameters().Get()
 
 	ttl := p.Duration(parameters.ReplayDialParametersTTL)
+	dslPlaceholderTTL := p.Duration(parameters.DSLPrioritizeDialPlaceholderTTL)
 
 	// Replay ignoring tactics changes with a probability allows for a mix of
 	// sticking with replay and exploring use of new tactics.
@@ -286,17 +289,15 @@ func MakeDialParameters(
 	// DSLPendingPrioritizeDial dial parameters record, and relying on the
 	// move-to-front logic in the server entry iterator shuffle.
 	//
-	// Once selected, reset the DSLPendingPrioritizeDial placeholder and
-	// select new dial parameters. The DSLPrioritizedDial field is set and
-	// used to record dsl_prioritized metrics indicating that the dial was
-	// DSL prioritized. The DSLPrioritizedDial flag is retained, and
-	// dsl_prioritized reported, as long as the dial parameters are
-	// successfully replayed. Once the replay ends, the
-	// DSLPrioritizedDial/dsl_prioritized state is dropped.
+	// The DSLPendingPrioritizeDial placeholder remains valid until it exceeds
+	// the configured TTL or is removed in Failed. When the placeholder is
+	// selected and attempted, new dial parameters are selected.
 	//
-	// Currently there is no specific TTL for a DSLPendingPrioritizeDial
-	// placeholder, since the iterator shuffle move-to-front has taken place
-	// already, before the dial parameters is unmarshaled.
+	// The DSLPrioritizedDial field is set and used to record dsl_prioritized
+	// metrics indicating that the dial was DSL prioritized. The
+	// DSLPrioritizedDial flag is retained, and dsl_prioritized reported, as
+	// long as the dial parameters are successfully replayed. Once the replay
+	// ends, the DSLPrioritizedDial/dsl_prioritized state is dropped.
 	//
 	// The isTactics case is not excluded from this DSLPrioritizedDial logic,
 	// since a DSLPendingPrioritizeDial placeholder may be created for a
@@ -305,11 +306,30 @@ func MakeDialParameters(
 	// server entry happens to have been used for a tunnel protocol. See
 	// fetchTactics.
 
-	DSLPendingPrioritizeDial := false
-	DSLPrioritizedDial := false
-	if dialParams != nil {
-		DSLPendingPrioritizeDial = dialParams.DSLPendingPrioritizeDial
-		DSLPrioritizedDial = dialParams.DSLPrioritizedDial
+	dslPendingPrioritizeDial :=
+		dialParams != nil && !dialParams.DSLPendingPrioritizeDialTimestamp.IsZero()
+
+	if dslPendingPrioritizeDial {
+
+		if dslPlaceholderTTL > 0 &&
+			dialParams.DSLPendingPrioritizeDialTimestamp.
+				Before(currentTimestamp.Add(-dslPlaceholderTTL)) {
+
+			// If the DSLPendingPrioritizeDial placeholder is expired, delete it.
+			//
+			// Limitation: at this point, the expired placeholder has already
+			// resulted in a server entry iterator move-to-front for one last
+			// round. For this reason, dslPendingPrioritizeDial isn't reset
+			// to false and dialParams.DSLPrioritizedDial will still be set to true.
+
+			err = DeleteDialParameters(serverEntry.IpAddress, networkID)
+			if err != nil {
+				NoticeWarning("DeleteDialParameters failed: %s", err)
+			}
+		}
+
+		// Replace the placeholder with new dial parameters.
+		dialParams = nil
 	}
 
 	// Check if replay is permitted:
@@ -325,7 +345,6 @@ func MakeDialParameters(
 	// When existing dial parameters don't meet these conditions, dialParams
 	// is reset to nil and new dial parameters will be generated.
 
-	var currentTimestamp time.Time
 	var configStateHash []byte
 	var serverEntryHash []byte
 	var configChanged bool
@@ -334,7 +353,6 @@ func MakeDialParameters(
 	// output DialParameters will not be stored by Success.
 
 	if ttl > 0 {
-		currentTimestamp = time.Now()
 		configStateHash, serverEntryHash = getDialStateHashes(config, p, serverEntry)
 
 		configChanged = dialParams != nil && !bytes.Equal(
@@ -345,9 +363,6 @@ func MakeDialParameters(
 		(ttl <= 0 ||
 			dialParams.LastUsedTimestamp.Before(currentTimestamp.Add(-ttl)) ||
 
-			// Replace DSL prioritize placeholder.
-			dialParams.DSLPendingPrioritizeDial ||
-
 			// Replay is disabled when the current config state hash -- config
 			// dial parameters and the current tactics tag -- have changed
 			// since the last dial. This prioritizes applying any potential
@@ -395,11 +410,12 @@ func MakeDialParameters(
 
 		// In these cases, existing dial parameters are expired or no longer
 		// match the config state and so are cleared to avoid rechecking them.
-
 		err = DeleteDialParameters(serverEntry.IpAddress, networkID)
 		if err != nil {
 			NoticeWarning("DeleteDialParameters failed: %s", err)
 		}
+
+		// Reselect new dial parameters.
 		dialParams = nil
 	}
 
@@ -466,13 +482,17 @@ func MakeDialParameters(
 	// replacing the pending placholder and retained as long as the dial
 	// parameters are replayed.
 	dialParams.DSLPrioritizedDial =
-		DSLPendingPrioritizeDial || (isReplay && DSLPrioritizedDial)
+		dslPendingPrioritizeDial || (isReplay && dialParams.DSLPrioritizedDial)
 
 	// Even when replaying, LastUsedTimestamp is updated to extend the TTL of
 	// replayed dial parameters which will be updated in the datastore upon
 	// success.
 
-	dialParams.LastUsedTimestamp = currentTimestamp
+	if ttl > 0 {
+		dialParams.LastUsedTimestamp = currentTimestamp
+	} else {
+		dialParams.LastUsedTimestamp = time.Time{}
+	}
 	dialParams.LastUsedConfigStateHash = configStateHash
 	dialParams.LastUsedServerEntryHash = serverEntryHash
 
@@ -1952,6 +1972,19 @@ func (dialParams *DialParameters) Failed(config *Config, dialErr error) {
 		}
 	}
 
+	// Also clear the DSLPendingPrioritizeDial placeholder, to prevent
+	// subsequent move-to-front, with a configurable probability to retain it.
+
+	if dialParams.DSLPrioritizedDial && !dialParams.IsReplay &&
+		!p.WeightedCoinFlip(parameters.DSLPrioritizeDialRetainFailedProbability) {
+
+		NoticeInfo("Delete DSL prioritize dial for %s", dialParams.ServerEntry.GetDiagnosticID())
+		err := DeleteDialParameters(dialParams.ServerEntry.IpAddress, dialParams.NetworkID)
+		if err != nil {
+			NoticeWarning("DeleteDialParameters failed: %s", err)
+		}
+	}
+
 	// When a failed tunnel dialed with steering IP, remove the corresponding
 	// cache entry to avoid continuously redialing a potentially blocked or
 	// degraded POP.

+ 207 - 15
psiphon/dialParameters_test.go

@@ -150,7 +150,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 	}
 	defer CloseDataStore()
 
-	serverEntries := makeMockServerEntries(tunnelProtocol, "CA", providerID, frontingProviderID, 100)
+	serverEntries := makeMockServerEntries(tunnelProtocol, "CA", providerID, frontingProviderID, 200)
 
 	canReplay := func(serverEntry *protocol.ServerEntry, replayProtocol string) bool {
 		return replayProtocol == tunnelProtocol
@@ -816,7 +816,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
 
-	if dialParams.DSLPendingPrioritizeDial || !dialParams.DSLPrioritizedDial {
+	if !dialParams.DSLPendingPrioritizeDialTimestamp.IsZero() || !dialParams.DSLPrioritizedDial {
 		t.Fatalf("unexpected DSL prioritize state")
 	}
 
@@ -832,7 +832,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
 
-	if dialParams.DSLPendingPrioritizeDial || !dialParams.DSLPrioritizedDial {
+	if !dialParams.DSLPendingPrioritizeDialTimestamp.IsZero() || !dialParams.DSLPrioritizedDial {
 		t.Fatalf("unexpected DSL prioritize state")
 	}
 
@@ -856,7 +856,7 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("MakeDialParameters failed: %s", err)
 	}
 
-	if dialParams.DSLPendingPrioritizeDial || !dialParams.DSLPrioritizedDial {
+	if !dialParams.DSLPendingPrioritizeDialTimestamp.IsZero() || !dialParams.DSLPrioritizedDial {
 		t.Fatalf("unexpected DSL prioritize state")
 	}
 
@@ -864,6 +864,109 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 		t.Fatalf("unexpected non-replay")
 	}
 
+	// Test: DSLPendingPrioritizeDial placeholder TTL and retain-on-failure behavior
+
+	applyParameters[parameters.DSLPrioritizeDialRetainFailedProbability] = 0.0
+	applyParameters[parameters.DSLPrioritizeDialPlaceholderTTL] = "1h"
+	err = clientConfig.SetParameters("tag10", false, applyParameters)
+	if err != nil {
+		t.Fatalf("SetParameters failed: %s", err)
+	}
+
+	expiredPlaceholder := &DialParameters{
+		DSLPendingPrioritizeDialTimestamp: time.Now().Add(-24 * time.Hour),
+	}
+	err = SetDialParameters(serverEntries[2].IpAddress, networkID, expiredPlaceholder)
+	if err != nil {
+		t.Fatalf("SetDialParameters failed: %s", err)
+	}
+
+	dialParams, err = MakeDialParameters(
+		clientConfig, steeringIPCache, nil, nil, nil, canReplay, selectProtocol, serverEntries[2], nil, nil, false, 0, 0)
+	if err != nil {
+		t.Fatalf("MakeDialParameters failed: %s", err)
+	}
+
+	// DSLPrioritizedDial is true even for expired placeholders...
+	if !dialParams.DSLPrioritizedDial {
+		t.Fatalf("unexpected DSL prioritize state")
+	}
+
+	// ...but the expired placeholder is then deleted.
+	storedDialParams, err := GetDialParameters(clientConfig, serverEntries[2].IpAddress, networkID)
+	if err != nil {
+		t.Fatalf("GetDialParameters failed: %s", err)
+	}
+	if storedDialParams != nil {
+		t.Fatalf("unexpected placeholder retained")
+	}
+
+	err = datastoreUpdate(func(tx *datastoreTx) error {
+		return dslPrioritizeDialServerEntry(
+			tx, networkID, []byte(serverEntries[3].IpAddress))
+	})
+	if err != nil {
+		t.Fatalf("dslPrioritizeDialServerEntry failed: %s", err)
+	}
+
+	dialParams, err = MakeDialParameters(
+		clientConfig, steeringIPCache, nil, nil, nil, canReplay, selectProtocol, serverEntries[3], nil, nil, false, 0, 0)
+	if err != nil {
+		t.Fatalf("MakeDialParameters failed: %s", err)
+	}
+
+	if !dialParams.DSLPrioritizedDial {
+		t.Fatalf("unexpected DSL prioritize state")
+	}
+
+	storedDialParams, err = GetDialParameters(clientConfig, serverEntries[3].IpAddress, networkID)
+	if err != nil {
+		t.Fatalf("GetDialParameters failed: %s", err)
+	}
+	if storedDialParams == nil || storedDialParams.DSLPendingPrioritizeDialTimestamp.IsZero() {
+		t.Fatalf("missing DSL prioritize placeholder")
+	}
+
+	dialParams.Failed(clientConfig, dialErr)
+
+	storedDialParams, err = GetDialParameters(clientConfig, serverEntries[3].IpAddress, networkID)
+	if err != nil {
+		t.Fatalf("GetDialParameters failed: %s", err)
+	}
+	if storedDialParams != nil {
+		t.Fatalf("unexpected placeholder retained after failure")
+	}
+
+	applyParameters[parameters.DSLPrioritizeDialRetainFailedProbability] = 1.0
+	err = clientConfig.SetParameters("tag11", false, applyParameters)
+	if err != nil {
+		t.Fatalf("SetParameters failed: %s", err)
+	}
+
+	err = datastoreUpdate(func(tx *datastoreTx) error {
+		return dslPrioritizeDialServerEntry(
+			tx, networkID, []byte(serverEntries[3].IpAddress))
+	})
+	if err != nil {
+		t.Fatalf("dslPrioritizeDialServerEntry failed: %s", err)
+	}
+
+	dialParams, err = MakeDialParameters(
+		clientConfig, steeringIPCache, nil, nil, nil, canReplay, selectProtocol, serverEntries[3], nil, nil, false, 0, 0)
+	if err != nil {
+		t.Fatalf("MakeDialParameters failed: %s", err)
+	}
+
+	dialParams.Failed(clientConfig, dialErr)
+
+	storedDialParams, err = GetDialParameters(clientConfig, serverEntries[3].IpAddress, networkID)
+	if err != nil {
+		t.Fatalf("GetDialParameters failed: %s", err)
+	}
+	if storedDialParams == nil || storedDialParams.DSLPendingPrioritizeDialTimestamp.IsZero() {
+		t.Fatalf("expected DSL prioritize placeholder")
+	}
+
 	// Test: iterator shuffles
 
 	for i, serverEntry := range serverEntries {
@@ -884,8 +987,17 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 			t.Fatalf("StoreServerEntry failed: %s", err)
 		}
 
+		// Clear any residual DialParameters from previous test cases
+
+		err = DeleteDialParameters(serverEntry.IpAddress, networkID)
+		if err != nil {
+			t.Fatalf("DeleteDialParameters failed: %s", err)
+		}
+
 		if i%10 == 0 {
 
+			// Pave a replay candidate
+
 			dialParams, err := MakeDialParameters(
 				clientConfig, steeringIPCache, nil, nil, nil, canReplay, selectProtocol, serverEntry, nil, nil, false, 0, 0)
 			if err != nil {
@@ -893,6 +1005,19 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 			}
 
 			dialParams.Succeeded()
+
+		} else if i%10 == 1 {
+
+			// Pave a DSL prioritize candidate
+
+			err := datastoreUpdate(func(tx *datastoreTx) error {
+				return dslPrioritizeDialServerEntry(
+					tx, networkID, []byte(serverEntry.IpAddress))
+			})
+			if err != nil {
+				t.Fatalf("dslPrioritizeDialServerEntry failed: %s", err)
+			}
+
 		}
 	}
 
@@ -907,9 +1032,9 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 			t.Fatalf("unexpected affinity server")
 		}
 
-		// Test: the first shuffle should move the replay candidates to the front
+		// Test: the first shuffle should move all the replay/DSL-prioritize candidates to the front
 
-		for j := 0; j < 10; j++ {
+		for j := 0; j < 20; j++ {
 
 			serverEntry, err := iterator.Next()
 			if err != nil {
@@ -922,17 +1047,78 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 				t.Fatalf("MakeDialParameters failed: %s", err)
 			}
 
-			if !dialParams.IsReplay {
-				t.Fatalf("unexpected non-replay")
+			if !dialParams.IsReplay && !dialParams.DSLPrioritizedDial {
+				t.Fatalf("unexpected non-replay/non-DSL-prioritized")
 			}
 		}
 
 		iterator.Reset()
 
-		// Test: subsequent shuffles should not move the replay candidates
+		// Test: subsequent shuffles should not move the replay/DSL-prioritize candidates candidates
+
+		allMoveToFront := true
+		for j := 0; j < 20; j++ {
+
+			serverEntry, err := iterator.Next()
+			if err != nil {
+				t.Fatalf("ServerEntryIterator.Next failed: %s", err)
+			}
+
+			dialParams, err := MakeDialParameters(
+				clientConfig, steeringIPCache, nil, nil, nil, canReplay, selectProtocol, serverEntry, nil, nil, false, 0, 0)
+			if err != nil {
+				t.Fatalf("MakeDialParameters failed: %s", err)
+			}
+
+			if !dialParams.IsReplay && !dialParams.DSLPrioritizedDial {
+				allMoveToFront = false
+			}
+		}
+
+		if allMoveToFront {
+			t.Fatalf("unexpected all replay/DSL-prioritized")
+		}
+
+		iterator.Close()
+
+		// Test: max move-to-front
+
+		applyParameters[parameters.ServerEntryIteratorMaxMoveToFront] = 5
+		applyParameters[parameters.ReplayIgnoreChangedConfigStateProbability] = 1.0
+		err = clientConfig.SetParameters("tag12a", false, applyParameters)
+		if err != nil {
+			t.Fatalf("SetParameters failed: %s", err)
+		}
+
+		hasAffinity, iterator, err = NewServerEntryIterator(clientConfig)
+		if err != nil {
+			t.Fatalf("NewServerEntryIterator failed: %s", err)
+		}
+
+		if hasAffinity {
+			t.Fatalf("unexpected affinity server")
+		}
+
+		for j := 0; j < 5; j++ {
+
+			serverEntry, err := iterator.Next()
+			if err != nil {
+				t.Fatalf("ServerEntryIterator.Next failed: %s", err)
+			}
+
+			dialParams, err := MakeDialParameters(
+				clientConfig, steeringIPCache, nil, nil, nil, canReplay, selectProtocol, serverEntry, nil, nil, false, 0, 0)
+			if err != nil {
+				t.Fatalf("MakeDialParameters failed: %s", err)
+			}
+
+			if !dialParams.IsReplay && !dialParams.DSLPrioritizedDial {
+				t.Fatalf("unexpected non-replay/non-DSL-prioritized")
+			}
+		}
 
-		allReplay := true
-		for j := 0; j < 10; j++ {
+		allMoveToFront = true
+		for j := 5; j < 20; j++ {
 
 			serverEntry, err := iterator.Next()
 			if err != nil {
@@ -945,16 +1131,22 @@ func runDialParametersAndReplay(t *testing.T, tunnelProtocol string) {
 				t.Fatalf("MakeDialParameters failed: %s", err)
 			}
 
-			if !dialParams.IsReplay {
-				allReplay = false
+			if !dialParams.IsReplay && !dialParams.DSLPrioritizedDial {
+				allMoveToFront = false
 			}
 		}
 
-		if allReplay {
-			t.Fatalf("unexpected all replay")
+		if allMoveToFront {
+			t.Fatalf("unexpected all replay/DSL-prioritized")
 		}
 
 		iterator.Close()
+
+		applyParameters[parameters.ServerEntryIteratorMaxMoveToFront] = -1
+		err = clientConfig.SetParameters("tag12b", false, applyParameters)
+		if err != nil {
+			t.Fatalf("SetParameters failed: %s", err)
+		}
 	}
 }
 

+ 1 - 1
psiphon/server/server_test.go

@@ -3686,7 +3686,7 @@ func checkExpectedDSLPendingPrioritizeDial(
 	}
 
 	if dialParams == nil ||
-		!dialParams.DSLPendingPrioritizeDial ||
+		dialParams.DSLPendingPrioritizeDialTimestamp.IsZero() ||
 		dialParams.DSLPrioritizedDial {
 
 		return errors.TraceNew("unexpected server entry state")