Giter VIP home page Giter VIP logo

Comments (12)

andersio avatar andersio commented on July 16, 2024

An issue that looked for the (almost, time-based though) same thing: #83

from reactiveswift.

mdiep avatar mdiep commented on July 16, 2024

Can you give a little more detail about what you're trying to do?

from reactiveswift.

sharplet avatar sharplet commented on July 16, 2024

The idea is basically to create a state machine, where an action will restart from the last point it failed:

let step1: SignalProducer<A, AnyError>
let step2: (A) -> SignalProducer<B, AnyError>
let step3: (B) -> SignalProducer<C, AnyError>

let task = step1
  .replaySuccessLazily() // don't love this name
  .flatMap(.concat, transform: step2)
  .replaySuccessLazily()
  .flatMap(.concat, transform: step3)
  .replaySuccessLazily()

let action = Action { _ in task }

from reactiveswift.

andersio avatar andersio commented on July 16, 2024

It seems the prominent need here is a mean to "fail gracefully" by invalidating the cached values and restarting the producer. Composing withretry(upTo:) can do the later but not the former.

It is like how #83 asked for a producer that invalidates the cached image upon a timeout event, and restarts the wrapped producer.

I'd say we may introduce a specialized replayLazily overload for something like Error == ReplayCacheInvalidation. #83 would be made possible by using timeout, and for this issue a mapError or flatMapError to ReplayCacheInvalidation.

from reactiveswift.

mdiep avatar mdiep commented on July 16, 2024

It seems like you should move the retries into the steps.

let step1: SignalProducer<A, AnyError>
let step2: (A) -> SignalProducer<B, AnyError>
let step3: (B) -> SignalProducer<C, AnyError>

let task = step1
  .retry(upTo: 3)
  .flatMap(.concat) { step2($0).retry(upTo: 3) }
  .flatMap(.concat) { step3($0).retry(upTo: 3) }

let action = Action { _ in task }

Would that work?

from reactiveswift.

sharplet avatar sharplet commented on July 16, 2024

I don't think retry() does what I want here, because it retries the producer automatically. What I'm after is a way to have a producer replay values and completion on start, but if the multicasted producer has failed then the cached events should be discarded and it should be like starting the producer for the first time.

from reactiveswift.

mdiep avatar mdiep commented on July 16, 2024

In my snippet above, step3 will be retried automatically—without restarting steps 1 or 2—if it fails, reusing the value from step 2. It sounded like that was what you wanted.

But if you really want to replay all values, then you'd need a custom operator like that. I can't think of a better name for it, so I'd probably add it like that to your project until/unless a it solidifies into something more clear.

from reactiveswift.

sharplet avatar sharplet commented on July 16, 2024

I'll be honest, I'm a little daunted by the complexity of replayLazily, and the idea of largely duplicating that in a new operator (or multiple!).

I've been doing some thinking about introducing the notion of a caching policy, and I'd like to spitball a possible approach here. I think it would be possible to extend replayLazily to take an optional parameter policy:

// current behaviour, and the default value
replayLazily(upTo: Int.max, policy: .alwaysReplay)

// behaviour described in this issue
replayLazily(upTo: Int.max, policy: .replayCompleted)

// behaviour described in #83
replayLazily(upTo: Int.max, policy: .timeout(after: .seconds(1), on: QueueScheduler()))

The policy would be defined something like this:

enum ReplayAction {
  case replay
  case invalidate
}

/// A function that allows responding to any event by invalidating the cache asynchronously
struct ReplayCachePolicy<Value, Error: Swift.Error> {
  let action: (_ event: Event<Value, Error>) -> SignalProducer<ReplayAction, NoError>
}

extension ReplayCachePolicy {
  static var alwaysReplay: ReplayCachePolicy {
    return .init { _ in
      SignalProducer(value: .replay)
    }
  }
  
  static func timeout(after interval: DispatchTimeInterval, on scheduler: DateScheduler) -> ReplayCachePolicy {
    return .init { event in
      guard event.isTerminating else {
        return SignalProducer(value: .replay)
      }
      
      return SignalProducer(value: .invalidate)
        .delay(interval, on: scheduler)
    }
  }
  
  static var replayCompleted: ReplayCachePolicy {
    return .init { event in
      switch event {
      case .value, .completed:
        return SignalProducer(value: .replay)
      case .failed .interrupted:
        return SignalProducer(value: .invalidate)
      }
    }
  }
}

Then, when starting the multicasted, replayed producer, observe the cache policy events something like this:

multicastedSignal
  .materialize()
  .flatMap(.merge) { policy.action($0).take(first: 1) }
  .filter { $0 == .invalidate }
  .take(first: 1)
  .observeValues { _ in
    // ensure replay state is reset, so that next time the producer is started, the underlying producer will be restarted instead of replayed
  }

What do you think? Would something like this be an appropriate change? Does it belong in a separate operator? Is it not worth the additional complexity?

from reactiveswift.

andersio avatar andersio commented on July 16, 2024

As I might have mentioned, a separated operator might not be necessary. Since for both your case and #83 the requirement is replaying invalidation & restarting on failure, we may provide a overload that acts differently with a specific instance of error.

// current behaviour, and the default value
producer.replayLazily(upTo: Int.max)

// behaviour described in this issue
producer
    .mapError { _ in ReplayCacheInvalidation() }
    .replayLazily(upTo: Int.max)

// behaviour described in #83
// (The producer is not of `NoError` error type.)
producer
    .mapError { _ in ReplayCacheInvalidation() }
    .timeout(10.0, raising: ReplayCacheInvalidation(), on: UIScheduler())
    .replayLazily(upTo: Int.max)

For this overload, upon receiving failure, it purges the cached values - so that new observers do not see the old values - and restarts the producer.

Edit: Expanded a bit on the code snippet for #83.

from reactiveswift.

mdiep avatar mdiep commented on July 16, 2024

I've been doing some thinking about introducing the notion of a caching policy, and I'd like to spitball a possible approach here. I think it would be possible to extend replayLazily to take an optional parameter policy:

That seems sensible to me. 👍

from reactiveswift.

gbrhaz avatar gbrhaz commented on July 16, 2024

@andersio What is ReplayCacheInvalidation and how does it invalidate the cache on failures?

from reactiveswift.

RuiAAPeres avatar RuiAAPeres commented on July 16, 2024

Hello. 👋 Thanks for opening this issue. Due to inactivity, we will soft close the issue. If you feel that it should remain open, please let us know. 😄

from reactiveswift.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.