public async void UpsertRaceConditionAgainstOtherClientWithHigherVersion()
        {
            if (Configuration.SetConcurrentModificationHookAction is null)
            {
                return;
            }

            var key = "key";
            int startVersion = 1, higherVersion = 3, attemptedVersion = 2;
            var startItem  = new TestEntity(key, startVersion, "value1");
            var higherItem = startItem.WithVersion(higherVersion).WithValue("value" + higherVersion);

            using (var store2 = CreateStoreImpl())
            {
                Action concurrentModifier = () =>
                {
                    AsyncUtils.WaitSafely(() => Upsert(store2, TestEntity.Kind, key,
                                                       higherItem.SerializedItemDescriptor));
                };

                var store1 = CreateStoreImplWithUpdateHook(concurrentModifier);
                await Init(store1, new DataBuilder().Add(TestEntity.Kind, startItem).BuildSerialized());

                var attemptedItem = startItem.WithVersion(attemptedVersion);
                await Upsert(store1, TestEntity.Kind, key, attemptedItem.SerializedItemDescriptor);

                AssertEqualsSerializedItem(higherItem, await Get(store1, TestEntity.Kind, key));
            }
        }
        public void VerifyUnrecoverableHttpError(int errorStatus)
        {
            var errorCondition = ServerErrorCondition.FromStatus(errorStatus);

            WithServerErrorCondition(errorCondition, StreamWithEmptyData, (uri, httpConfig, recorder) =>
            {
                using (var dataSource = MakeDataSource(uri, BasicUser,
                                                       c => c.DataSource(Components.StreamingDataSource().InitialReconnectDelay(TimeSpan.Zero))
                                                       .Http(httpConfig)))
                {
                    var initTask = dataSource.Start();
                    var status   = _updateSink.ExpectStatusUpdate();
                    errorCondition.VerifyDataSourceStatusError(status);

                    _updateSink.ExpectNoMoreActions();

                    recorder.RequireRequest();
                    recorder.RequireNoRequests(TimeSpan.FromMilliseconds(100));

                    Assert.True(AsyncUtils.WaitSafely(() => initTask, TimeSpan.FromSeconds(1)));

                    errorCondition.VerifyLogMessage(logCapture);
                }
            });
        }
        public async void UpsertRaceConditionAgainstOtherClientWithLowerVersion()
        {
            if (Configuration.SetConcurrentModificationHookAction is null)
            {
                return;
            }

            var key = "key";
            int startVersion = 1, store2VersionStart = 2, store2VersionEnd = 4, store1VersionEnd = 10;
            var startItem = new TestEntity(key, startVersion, "value1");

            using (var store2 = CreateStoreImpl())
            {
                int    versionCounter     = store2VersionStart;
                Action concurrentModifier = () =>
                {
                    if (versionCounter <= store2VersionEnd)
                    {
                        AsyncUtils.WaitSafely(() => Upsert(store2, TestEntity.Kind, key,
                                                           startItem.WithVersion(versionCounter).WithValue("value" + versionCounter).SerializedItemDescriptor));
                        versionCounter++;
                    }
                };

                var store1 = CreateStoreImplWithUpdateHook(concurrentModifier);
                await Init(store1, new DataBuilder().Add(TestEntity.Kind, startItem).BuildSerialized());

                var endItem = startItem.WithVersion(store1VersionEnd).WithValue("value" + store1VersionEnd);
                await Upsert(store1, TestEntity.Kind, key, endItem.SerializedItemDescriptor);

                AssertEqualsSerializedItem(endItem, await Get(store1, TestEntity.Kind, key));
            }
        }
示例#4
0
        private IMembership QueryMembership(string userKey)
        {
            var hash = BigSegmentUserKeyHash(userKey);

            _logger.Debug("Querying Big Segment state for user hash {0}", hash);
            return(AsyncUtils.WaitSafely(() => _store.GetMembershipAsync(hash)));
        }
        public void VerifyRecoverableHttpError(int errorStatus)
        {
            var errorCondition = ServerErrorCondition.FromStatus(errorStatus);

            WithServerErrorCondition(errorCondition, StreamWithEmptyData, (uri, httpConfig, recorder) =>
            {
                using (var dataSource = MakeDataSource(uri, BasicUser,
                                                       c => c.DataSource(Components.StreamingDataSource().InitialReconnectDelay(TimeSpan.Zero))
                                                       .Http(httpConfig)))
                {
                    var initTask = dataSource.Start();

                    var status = _updateSink.ExpectStatusUpdate();
                    errorCondition.VerifyDataSourceStatusError(status);

                    // We don't check here for a second status update to the Valid state, because that was
                    // done by DataSourceUpdatesImpl when Init was called - our test fixture doesn't do it.

                    _updateSink.ExpectInit(BasicUser);

                    recorder.RequireRequest();
                    recorder.RequireRequest();

                    Assert.True(AsyncUtils.WaitSafely(() => initTask, TimeSpan.FromSeconds(1)));

                    errorCondition.VerifyLogMessage(logCapture);
                }
            });
        }
        public void PingCausesPoll()
        {
            var data = new DataSetBuilder()
                       .Add("flag1", 1, LdValue.Of(true), 0)
                       .Build();
            var streamWithPing = Handlers.SSE.Start()
                                 .Then(PingEvent)
                                 .Then(Handlers.SSE.LeaveOpen());

            using (var pollingServer = HttpServer.Start(PollingResponse(data)))
            {
                using (var streamingServer = HttpServer.Start(streamWithPing))
                {
                    using (var dataSource = MakeDataSource(streamingServer.Uri, BasicUser,
                                                           c => c.ServiceEndpoints(Components.ServiceEndpoints()
                                                                                   .Streaming(streamingServer.Uri).Polling(pollingServer.Uri))))
                    {
                        var initTask = dataSource.Start();

                        pollingServer.Recorder.RequireRequest();

                        var receivedData = _updateSink.ExpectInit(BasicUser);
                        AssertHelpers.DataSetsEqual(data, receivedData);

                        Assert.True(AsyncUtils.WaitSafely(() => initTask, TimeSpan.FromSeconds(1)));
                        Assert.False(initTask.IsFaulted);
                        Assert.True(dataSource.Initialized);
                    }
                }
            }
        }
示例#7
0
        /// <inheritdoc/>
        public bool Identify(User user, TimeSpan maxWaitTime)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            return(AsyncUtils.WaitSafely(() => IdentifyAsync(user), maxWaitTime));
        }
示例#8
0
        void Start(TimeSpan maxWaitTime)
        {
            var success = AsyncUtils.WaitSafely(() => _connectionManager.Start(), maxWaitTime);

            if (!success)
            {
                _log.Warn("Client did not successfully initialize within {0} milliseconds.",
                          maxWaitTime.TotalMilliseconds);
            }
        }
示例#9
0
        protected FluentMockServer MakeServer()
        {
            // currently we don't need to customize any server settings
            var server = FluentMockServer.Start();

            // Perform an initial request to make sure the server has warmed up. On Android in particular, startup
            // of the very first server instance in the test run seems to be very slow, which may cause the first
            // request made by unit tests to time out.
            using (var client = new HttpClient())
            {
                AsyncUtils.WaitSafely(() => client.GetAsync(server.Urls[0]));
            }
            server.ResetLogEntries(); // so the initial request doesn't interfere with test postconditions
            return(server);
        }
        public void PutCausesDataToBeStoredAndDataSourceInitialized()
        {
            var data = new DataSetBuilder()
                       .Add("flag1", 1, LdValue.Of(true), 0)
                       .Build();

            WithDataSourceAndServer(StreamWithInitialData(data), (dataSource, _, initTask) =>
            {
                var receivedData = _updateSink.ExpectInit(BasicUser);
                AssertHelpers.DataSetsEqual(data, receivedData);

                Assert.True(AsyncUtils.WaitSafely(() => initTask, TimeSpan.FromSeconds(1)));
                Assert.False(initTask.IsFaulted);
                Assert.True(dataSource.Initialized);
            });
        }
        public void SuccessfulRequestCausesDataToBeStoredAndDataSourceInitialized()
        {
            using (var server = HttpServer.Start(PollingResponse(AllData)))
            {
                using (var dataSource = MakeDataSource(server.Uri, BasicUser))
                {
                    var initTask = dataSource.Start();

                    var receivedData = _updateSink.ExpectInit(BasicUser);
                    AssertHelpers.DataSetsEqual(AllData, receivedData);

                    Assert.True(AsyncUtils.WaitSafely(() => initTask, TimeSpan.FromSeconds(1)));
                    Assert.False(initTask.IsFaulted);
                    Assert.True(dataSource.Initialized);
                }
            }
        }
示例#12
0
        /// <summary>
        /// Returns a BigSegmentStoreStatus describing whether the store seems to be available
        /// (that is, the last query to it did not return an error) and whether it is stale (that is, the last
        /// known update time is too far in the past).
        /// </summary>
        /// <remarks>
        /// If we have not yet obtained that information (the poll task has not executed yet), then this method
        /// immediately does a metadata query and waits for it to succeed or fail. This means that if an
        /// application using Big Segments evaluates a feature flag immediately after creating the SDK
        /// client, before the first status poll has happened, that evaluation may block for however long it
        /// takes to query the store.
        /// </remarks>
        /// <returns>the store status</returns>
        internal BigSegmentStoreStatus GetStatus()
        {
            BigSegmentStoreStatus?ret;

            _lock.EnterReadLock();
            try
            {
                ret = _lastStatus;
            }
            finally
            {
                _lock.ExitReadLock();
            }
            if (ret.HasValue)
            {
                return(ret.Value);
            }
            return(AsyncUtils.WaitSafely(() => PollStoreAndUpdateStatusAsync()));
        }
示例#13
0
 public bool WaitFor(DataSourceState desiredState, TimeSpan timeout) =>
 AsyncUtils.WaitSafely(() => _dataSourceUpdates.WaitForAsync(desiredState, timeout));
示例#14
0
 /// <inheritdoc/>
 public bool SetOffline(bool value, TimeSpan maxWaitTime)
 {
     return(AsyncUtils.WaitSafely(() => SetOfflineAsync(value), maxWaitTime));
 }