| 115 | } |
| 116 | |
| 117 | ACTOR Future<bool> ClusterConnectionKey::persistImpl(Reference<ClusterConnectionKey> self) { |
| 118 | self->setPersisted(); |
| 119 | state Value newConnectionString = ValueRef(self->cs.toString()); |
| 120 | |
| 121 | try { |
| 122 | state Transaction tr(self->db); |
| 123 | loop { |
| 124 | try { |
| 125 | Optional<Value> existingConnectionString = wait(tr.get(self->connectionStringKey)); |
| 126 | // Someone has already updated the connection string to what we want |
| 127 | if (existingConnectionString.present() && existingConnectionString.get() == newConnectionString) { |
| 128 | self->lastPersistedConnectionString = newConnectionString; |
| 129 | return true; |
| 130 | } |
| 131 | // Someone has updated the connection string to something we didn't expect, in which case we leave it |
| 132 | // alone. It's possible this could result in the stored string getting stuck if the connection string |
| 133 | // changes twice and only the first change is recorded. If the process that wrote the first change dies |
| 134 | // and no other process attempts to write the intermediate state, then only a newly opened connection |
| 135 | // key would be able to update the state. |
| 136 | else if (existingConnectionString.present() && |
| 137 | existingConnectionString != self->lastPersistedConnectionString) { |
| 138 | TraceEvent(SevWarnAlways, "UnableToChangeConnectionKeyDueToMismatch") |
| 139 | .detail("ConnectionKey", self->connectionStringKey) |
| 140 | .detail("NewConnectionString", newConnectionString) |
| 141 | .detail("ExpectedStoredConnectionString", self->lastPersistedConnectionString) |
| 142 | .detail("ActualStoredConnectionString", existingConnectionString); |
| 143 | return false; |
| 144 | } |
| 145 | tr.set(self->connectionStringKey, newConnectionString); |
| 146 | wait(tr.commit()); |
| 147 | |
| 148 | self->lastPersistedConnectionString = newConnectionString; |
| 149 | return true; |
| 150 | } catch (Error& e) { |
| 151 | wait(tr.onError(e)); |
| 152 | } |
| 153 | } |