Skip to content

Commit ae808a4

Browse files
committed
Skip Release for sessions that were not mutated
The file provider (and others that persist via Encode in Release) writes the session unconditionally at the end of every request. Two concurrent reads of the same session ID then race: each request encodes its stale snapshot and writes it back, so an in-flight mutation from a third request can be clobbered. Track Set/Delete/Flush calls on a wrapper around the per-request RawStore and skip Release when nothing was written. Reads no longer touch the backing store, eliminating the race for read-only traffic.
1 parent ab7cb8b commit ae808a4

1 file changed

Lines changed: 30 additions & 1 deletion

File tree

session.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,31 @@ type store struct {
7171

7272
var _ Store = &store{}
7373

74+
// dirtyTracker wraps a RawStore and records whether the request mutated the
75+
// session. Sessioner uses this to skip Release on read-only requests so two
76+
// concurrent reads of the same session ID cannot race to clobber each other's
77+
// view of the data on disk (file provider encode-and-writes unconditionally
78+
// in Release).
79+
type dirtyTracker struct {
80+
RawStore
81+
dirty bool
82+
}
83+
84+
func (d *dirtyTracker) Set(key, val interface{}) error {
85+
d.dirty = true
86+
return d.RawStore.Set(key, val)
87+
}
88+
89+
func (d *dirtyTracker) Delete(key interface{}) error {
90+
d.dirty = true
91+
return d.RawStore.Delete(key)
92+
}
93+
94+
func (d *dirtyTracker) Flush() error {
95+
d.dirty = true
96+
return d.RawStore.Flush()
97+
}
98+
7499
// Options represents a struct for specifying configuration options for the session middleware.
75100
type Options struct {
76101
// Name of provider. Default is "memory".
@@ -187,8 +212,9 @@ func Sessioner(options ...Options) macaron.Handler {
187212
})
188213

189214
ctx.Map(f)
215+
tracker := &dirtyTracker{RawStore: sess}
190216
s := store{
191-
RawStore: sess,
217+
RawStore: tracker,
192218
Manager: manager,
193219
}
194220

@@ -200,6 +226,9 @@ func Sessioner(options ...Options) macaron.Handler {
200226
return
201227
}
202228

229+
if !tracker.dirty {
230+
return
231+
}
203232
if err = sess.Release(); err != nil {
204233
panic("session(release): " + err.Error())
205234
}

0 commit comments

Comments
 (0)