// Tick constructs a ticker with interval, and calls the given ProcessFunc every // time the ticker fires. // This is sequentially rate limited, only one call will be in-flight at a time. // // p := periodicproc.Tick(time.Second, func(proc goprocess.Process) { // fmt.Println("fire!") // }) // // <-time.After(3 * time.Second) // p.Close() // // // Output: // // fire! // // fire! // // fire! func Tick(interval time.Duration, procfunc gp.ProcessFunc) gp.Process { return gp.Go(func(proc gp.Process) { ticker := time.NewTicker(interval) callOnTicker(ticker.C, procfunc)(proc) ticker.Stop() }) }
// EveryGo calls the given ProcessFunc at periodic intervals. Internally, it uses // <-time.After(interval) // This is not rate limited, multiple calls could be in-flight at the same time. func EveryGo(interval time.Duration, procfunc gp.ProcessFunc) gp.Process { return gp.Go(func(proc gp.Process) { for { select { case <-time.After(interval): proc.Go(procfunc) case <-proc.Closing(): // we're told to close return } } }) }
// OnSignalGo calls the given ProcessFunc every time the signal fires. // This is not rate limited, multiple calls could be in-flight at the same time. // // sig := make(chan struct{}) // p := periodicproc.OnSignalGo(sig, func(proc goprocess.Process) { // fmt.Println("fire!") // <-time.After(time.Second) // wont block execution // }) // // sig<- struct{} // sig<- struct{} // sig<- struct{} // // // Output: // // fire! // // fire! // // fire! func OnSignalGo(sig <-chan struct{}, procfunc gp.ProcessFunc) gp.Process { return gp.Go(func(proc gp.Process) { for { select { case <-sig: proc.Go(procfunc) case <-proc.Closing(): // we're told to close return } } }) }
// Every calls the given ProcessFunc at periodic intervals. Internally, it uses // <-time.After(interval), so it will have the behavior of waiting _at least_ // interval in between calls. If you'd prefer the time.Ticker behavior, use // periodicproc.Tick instead. // This is sequentially rate limited, only one call will be in-flight at a time. func Every(interval time.Duration, procfunc gp.ProcessFunc) gp.Process { return gp.Go(func(proc gp.Process) { for { select { case <-time.After(interval): select { case <-proc.Go(procfunc).Closed(): // spin it out as a child, and wait till it's done. case <-proc.Closing(): // we're told to close return } case <-proc.Closing(): // we're told to close return } } }) }
// OnSignal calls the given ProcessFunc every time the signal fires, and waits for it to exit. // This is sequentially rate limited, only one call will be in-flight at a time. // // sig := make(chan struct{}) // p := periodicproc.OnSignal(sig, func(proc goprocess.Process) { // fmt.Println("fire!") // <-time.After(time.Second) // delays sequential execution by 1 second // }) // // sig<- struct{} // sig<- struct{} // sig<- struct{} // // // Output: // // fire! // // fire! // // fire! func OnSignal(sig <-chan struct{}, procfunc gp.ProcessFunc) gp.Process { return gp.Go(func(proc gp.Process) { for { select { case <-sig: select { case <-proc.Go(procfunc).Closed(): // spin it out as a child, and wait till it's done. case <-proc.Closing(): // we're told to close return } case <-proc.Closing(): // we're told to close return } } }) }
// TickerGo calls the given ProcessFunc every time the ticker fires. // This is not rate limited, multiple calls could be in-flight at the same time. func TickerGo(ticker <-chan time.Time, procfunc gp.ProcessFunc) gp.Process { return gp.Go(goCallOnTicker(ticker, procfunc)) }
func TestRepublish(t *testing.T) { // set cache life to zero for testing low-period repubs ctx, cancel := context.WithCancel(context.Background()) defer cancel() // create network mn := mocknet.New(ctx) var nodes []*core.IpfsNode for i := 0; i < 10; i++ { nd, err := core.NewNode(ctx, &core.BuildCfg{ Online: true, Host: mock.MockHostOption(mn), }) if err != nil { t.Fatal(err) } nd.Namesys = namesys.NewNameSystem(nd.Routing, nd.Repo.Datastore(), 0) nodes = append(nodes, nd) } mn.LinkAll() bsinf := core.BootstrapConfigWithPeers( []peer.PeerInfo{ nodes[0].Peerstore.PeerInfo(nodes[0].Identity), }, ) for _, n := range nodes[1:] { if err := n.Bootstrap(bsinf); err != nil { t.Fatal(err) } } // have one node publish a record that is valid for 1 second publisher := nodes[3] p := path.FromString("/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") // does not need to be valid rp := namesys.NewRoutingPublisher(publisher.Routing, publisher.Repo.Datastore()) err := rp.PublishWithEOL(ctx, publisher.PrivateKey, p, time.Now().Add(time.Second)) if err != nil { t.Fatal(err) } name := "/ipns/" + publisher.Identity.Pretty() if err := verifyResolution(nodes, name, p); err != nil { t.Fatal(err) } // Now wait a second, the records will be invalid and we should fail to resolve time.Sleep(time.Second) if err := verifyResolutionFails(nodes, name); err != nil { t.Fatal(err) } // The republishers that are contained within the nodes have their timeout set // to 12 hours. Instead of trying to tweak those, we're just going to pretend // they dont exist and make our own. repub := NewRepublisher(publisher.Routing, publisher.Repo.Datastore(), publisher.Peerstore) repub.Interval = time.Second repub.RecordLifetime = time.Second * 5 repub.AddName(publisher.Identity) proc := goprocess.Go(repub.Run) defer proc.Close() // now wait a couple seconds for it to fire time.Sleep(time.Second * 2) // we should be able to resolve them now if err := verifyResolution(nodes, name, p); err != nil { t.Fatal(err) } }